diff --git a/03 Writing Algorithms/12 Universes/03 Equity/04 Chained Universes/05 Chain Fundamental and Alternative Data.html b/03 Writing Algorithms/12 Universes/03 Equity/04 Chained Universes/05 Chain Fundamental and Alternative Data.html index 13211630a6..c6a7a405a7 100644 --- a/03 Writing Algorithms/12 Universes/03 Equity/04 Chained Universes/05 Chain Fundamental and Alternative Data.html +++ b/03 Writing Algorithms/12 Universes/03 Equity/04 Chained Universes/05 Chain Fundamental and Alternative Data.html @@ -7,90 +7,96 @@ QuiverCNBCsUniverse alternative universe - . It first selects the 100 most liquid US Equities and then filters them down to those mentioned by CNBC commentator/trader Jim Cramer. The output of the alternative universe selection method is the output of the chained universe. + . It stores every US Equity fundamental, intersects them with the names CNBC commentator Jim Cramer mentions, and trades the 100 most liquid intersection members each morning.

public class ChainedUniverseAlgorithm : QCAlgorithm
 {
-    private List<Symbol> _fundamental = new();
+    private List<Fundamental> _fundamental = [];
+    private Universe _universe;
 
     public override void Initialize()
     {
         SetStartDate(2024, 9, 1);
         SetEndDate(2024, 12, 31);
         SetCash(100000);
-
-        // Filter the top 100 liquid equities of the last trading day, and save the symbols for the next filtering.
+        Settings.SeedInitialPrices = true;
+        UniverseSettings.Resolution = Resolution.Minute;
+        // First universe: store all US Equity fundamentals; emits Universe.Unchanged.
         AddUniverse(fundamental =>
         {
-            _fundamental = (from c in fundamental
-                orderby c.DollarVolume descending
-                select c.Symbol).Take(100).ToList();
+            _fundamental = [..fundamental];
             return Universe.Unchanged;
         });
-        // Filter the equities being commented on by CNBC analyst Cramer, then select the ones that intersect with the fundamental universe.
-        AddUniverse<QuiverCNBCsUniverse>(altCoarse =>
+        // Second universe: equities mentioned by Cramer, ranked by dollar volume.
+        _universe = AddUniverse<QuiverCNBCsUniverse>(altCoarse =>
         {
-            var followers = from d in altCoarse.OfType<QuiverCNBCsUniverse>()
-                where d.Traders.ToLower().Contains("cramer")
-                select d.Symbol;
-            return _fundamental.Intersect(followers);
+            var alt = altCoarse.OfType<QuiverCNBCsUniverse>()
+                .Where(d => d.Traders.ToLower().Contains("cramer"))
+                .Select(d => d.Symbol)
+                .ToHashSet();
+            Plot("Universe", "Raw", alt.Count);
+            return _fundamental
+                .Where(c => alt.Contains(c.Symbol))
+                .OrderByDescending(c => c.DollarVolume)
+                .Select(c => c.Symbol)
+                .Take(100);
         });
+        // Rebalance before market open to trade today's intersection.
+        Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance);
     }
 
-    public override void OnSecuritiesChanged(SecurityChanges changes)
-    {
-        // Request CNBC data for the selected stocks.
-        foreach (var added in changes.AddedSecurities)
-        {
-            AddData<QuiverCNBCs>(added.Symbol);
-        }
-    }
-
-    public override void OnData(Slice data)
+    private void Rebalance()
     {
-        foreach (var dataPoint in data.Get<QuiverCNBCs>().SelectMany(x=> x.Value.OfType<QuiverCNBC>()))
+        if (_universe.Selected.Count == 0)
         {
-            Debug($"{dataPoint.Symbol} traders at {data.Time}: {dataPoint.Traders}");
+            return;
         }
+        var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m;
+        var targets = _universe.Selected
+            .Select(symbol => new PortfolioTarget(symbol, weight))
+            .ToList();
+        SetHoldings(targets, true);
     }
 }
from AlgorithmImports import *
 
+
 class ChainedUniverseAlgorithm(QCAlgorithm):
 
-    _fundamental = []
+    _fundamental: List[Fundamental] = []
 
     def initialize(self) -> None:
         self.set_start_date(2024, 9, 1)
         self.set_end_date(2024, 12, 31)
         self.set_cash(100000)
-        self.add_universe(self._fundamental_filter_function)
-        self.add_universe(QuiverCNBCsUniverse, self._mad_money_selection)
-    
-    def _fundamental_filter_function(self, fundamental: List[Fundamental]) -> List[Symbol]:
-        # Filter the top 100 liquid equities of the last trading day, and save the symbols for the next filtering.
-        sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume, reverse=True) 
-        self._fundamental = [c.symbol for c in sorted_by_dollar_volume[:100]]
+        self.settings.seed_initial_prices = True
+        self.universe_settings.resolution = Resolution.MINUTE
+        # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED.
+        self.add_universe(self._fundamental_filter)
+        # Second universe: equities mentioned by Cramer, ranked by dollar volume.
+        self._universe = self.add_universe(QuiverCNBCsUniverse, self._select_assets)
+        # Rebalance before market open to trade today's intersection.
+        self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.at(9, 0, 0), self._rebalance)
+
+    def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse:
+        self._fundamental = fundamental
         return Universe.UNCHANGED
-    
-    def _mad_money_selection(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]:
-        # Filter the equities being commented on by CNBC analyst Cramer, then select the ones that intersect with the fundamental universe.
-        madmoney = [d.symbol for d in alt_coarse if 'Cramer' in d.traders]
-        return list(set(self._fundamental) & set(madmoney))
-    
-    def on_securities_changed(self, changes: SecurityChanges) -> None:
-        # Request CNBC data for the selected stocks.
-        for added in changes.added_securities:
-            self.add_data(QuiverCNBCs, added.symbol)
-    
-    def on_data(self, data: Slice) -> None:
-        # Prices in the slice from the universe selection
-        # Alternative data in a slice from OnSecuritiesChanged Addition
-        # for ticker,bar in data.bars.items():
-        #     pass
-        for dataset_symbol, data_points in data.get(QuiverCNBCs).items():
-            for data_point in data_points:
-                self.debug(f"{dataset_symbol} traders at {data.time}: {data_point.traders}")
+
+    def _select_assets(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]:
+        # Keep symbols mentioned by Cramer.
+        alt = {d.symbol for d in alt_coarse if 'cramer' in d.traders.lower()}
+        self.plot('Universe', 'Raw', len(alt))
+        return [c.symbol for c in sorted(
+            [c for c in self._fundamental if c.symbol in alt],
+            key=lambda c: c.dollar_volume, reverse=True
+        )[:100]]
+
+    def _rebalance(self) -> None:
+        if not self._universe.selected:
+            return
+        weight = min(1 / len(self._universe.selected), 0.1)
+        targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected]
+        self.set_holdings(targets, True)
 
diff --git a/03 Writing Algorithms/12 Universes/03 Equity/04 Chained Universes/08 Chain ETF and Alternative Data.html b/03 Writing Algorithms/12 Universes/03 Equity/04 Chained Universes/08 Chain ETF and Alternative Data.html index 0db2580561..85d50261b6 100644 --- a/03 Writing Algorithms/12 Universes/03 Equity/04 Chained Universes/08 Chain ETF and Alternative Data.html +++ b/03 Writing Algorithms/12 Universes/03 Equity/04 Chained Universes/08 Chain ETF and Alternative Data.html @@ -7,90 +7,98 @@ QuiverCNBCsUniverse alternative universe - . It first selects all constituents of SPY and then filters them down to those mentioned by CNBC commentator/trader Jim Cramer. The output of the alternative universe selection method is the output of the chained universe. + . It stores every SPY constituent, intersects them with the names CNBC commentator Jim Cramer mentions, and trades the 100 heaviest-weighted intersection members each morning. Names CNBC mentions but that aren't in SPY are dropped.

public class ChainedUniverseAlgorithm : QCAlgorithm
 {
-    private List<Symbol> _etf = new();
+    private List<ETFConstituentUniverse> _etf = [];
+    private Universe _universe;
 
     public override void Initialize()
     {
         SetStartDate(2024, 9, 1);
         SetEndDate(2024, 12, 31);
         SetCash(100000);
-        UniverseSettings.Asynchronous = true;
-
-        // Save all SPY constituents for the next filtering.
+        Settings.SeedInitialPrices = true;
+        UniverseSettings.Resolution = Resolution.Minute;
+        // First universe: store all SPY constituents; emits Universe.Unchanged.
         AddUniverse(Universe.ETF("SPY", Market.USA, UniverseSettings, constituents =>
         {
-            _etf = constituents.Select(c => c.Symbol).ToList();
+            _etf = [..constituents];
             return Universe.Unchanged;
         }));
-        // Filter the equities being commented on by CNBC analyst Cramer, then select the ones in SPY constituents.
-        AddUniverse<QuiverCNBCsUniverse>(altCoarse =>
+        // Second universe: equities mentioned by Cramer that are in SPY, ranked by ETF weight.
+        _universe = AddUniverse<QuiverCNBCsUniverse>(altCoarse =>
         {
-            var followers = from d in altCoarse.OfType<QuiverCNBCsUniverse>()
-                where d.Traders.ToLower().Contains("cramer")
-                select d.Symbol;
-            return _etf.Intersect(followers);
+            var alt = altCoarse.OfType<QuiverCNBCsUniverse>()
+                .Where(d => d.Traders.ToLower().Contains("cramer"))
+                .Select(d => d.Symbol)
+                .ToHashSet();
+            Plot("Universe", "Raw", alt.Count);
+            // Names not in SPY cannot be traded, so intersect with the ETF list.
+            return _etf
+                .Where(c => alt.Contains(c.Symbol))
+                .OrderByDescending(c => c.Weight)
+                .Select(c => c.Symbol)
+                .Take(100);
         });
+        // Rebalance before market open to trade today's intersection.
+        Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance);
     }
 
-    public override void OnSecuritiesChanged(SecurityChanges changes)
-    {
-        // Request CNBC data for the selected stocks.
-        foreach (var added in changes.AddedSecurities)
-        {
-            AddData<QuiverCNBCs>(added.Symbol);
-        }
-    }
-
-    public override void OnData(Slice data)
+    private void Rebalance()
     {
-        foreach (var dataPoint in data.Get<QuiverCNBCs>().SelectMany(x=> x.Value.OfType<QuiverCNBC>()))
+        if (_universe.Selected.Count == 0)
         {
-            Debug($"{dataPoint.Symbol} traders at {data.Time}: {dataPoint.Traders}");
+            return;
         }
+        var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m;
+        var targets = _universe.Selected
+            .Select(symbol => new PortfolioTarget(symbol, weight))
+            .ToList();
+        SetHoldings(targets, true);
     }
 }
from AlgorithmImports import *
 
+
 class ChainedUniverseAlgorithm(QCAlgorithm):
 
-    _etf = []
+    _etf: List[ETFConstituentUniverse] = []
 
-    def initialize(self):
+    def initialize(self) -> None:
         self.set_start_date(2024, 9, 1)
         self.set_end_date(2024, 12, 31)
         self.set_cash(100000)
-        self.universe_settings.asynchronous = True
-        # Save all SPY constituents for the next filtering.
-        self.add_universe(self.universe.etf("SPY", Market.USA, self.universe_settings, self._etf_constituents_filter))
-        # Next filtering based on CNBC data.
-        self.add_universe(QuiverCNBCsUniverse, self._mad_money_selection)
+        self.settings.seed_initial_prices = True
+        self.universe_settings.resolution = Resolution.MINUTE
+        # First universe: store all SPY constituents; emits Universe.UNCHANGED.
+        self.add_universe(self.universe.etf("SPY", Market.USA, self.universe_settings, self._etf_filter))
+        # Second universe: equities mentioned by Cramer that are in SPY, ranked by ETF weight.
+        self._universe = self.add_universe(QuiverCNBCsUniverse, self._select_assets)
+        # Rebalance before market open to trade today's intersection.
+        self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.at(9, 0, 0), self._rebalance)
 
-    def _etf_constituents_filter(self, fundamental: List[Fundamental]) -> List[Symbol]:
-        # Save all SPY constituents for the next filtering.
-        self._etf = [c.symbol for c in constituents]
+    def _etf_filter(self, constituents: List[ETFConstituentUniverse]) -> Universe.UnchangedUniverse:
+        self._etf = constituents
         return Universe.UNCHANGED
 
-    def _mad_money_selection(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]:
-        # Filter the equities being commented on by CNBC analyst Cramer, then select the ones in SPY constituents.
-        madmoney = [d.symbol for d in alt_coarse if 'Cramer' in d.traders]
-        return list(set(self._etf) & set(madmoney))
-
-    def on_securities_changed(self, changes):
-        # Request CNBC data for the selected stocks.
-        for added in changes.added_securities:
-            self.add_data(QuiverCNBCs, added.symbol)
+    def _select_assets(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]:
+        # Keep symbols mentioned by Cramer.
+        alt = {d.symbol for d in alt_coarse if 'cramer' in d.traders.lower()}
+        self.plot('Universe', 'Raw', len(alt))
+        # Names not in SPY cannot be traded, so intersect with the ETF list.
+        return [c.symbol for c in sorted(
+            [c for c in self._etf if c.symbol in alt],
+            key=lambda c: c.weight, reverse=True
+        )[:100]]
 
-    def on_data(self, data):
-        # Prices in the slice from the universe selection
-        # Alternative data in a slice from OnSecuritiesChanged Addition
-        # for ticker,bar in data.bars.items():
-        #     pass
-        for dataset_symbol, data_points in data.get(QuiverCNBCs).items():
-            for data_point in data_points:
-                self.debug(f"{dataset_symbol} traders at {data.time}: {data_point.traders}")
+ def _rebalance(self) -> None: + if not self._universe.selected: + return + weight = min(1 / len(self._universe.selected), 0.1) + targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] + self.set_holdings(targets, True) +
diff --git a/project-templates/csharp/alternative-data-chain-universe-braincompanyfilinglanguagemetricsuniverseall/Main.cs b/project-templates/csharp/alternative-data-chain-universe-braincompanyfilinglanguagemetricsuniverseall/Main.cs index 9fc8ef096b..d5308057ca 100644 --- a/project-templates/csharp/alternative-data-chain-universe-braincompanyfilinglanguagemetricsuniverseall/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-braincompanyfilinglanguagemetricsuniverseall/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class BrainCompanyFilingLanguageMetricsChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = []; + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -19,23 +19,28 @@ public override void Initialize() SetCash(100000); Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: positive sentiment in latest SEC filings, intersected with the fundamental list. + // Second universe: positive sentiment in latest SEC filings, ranked by dollar volume. _universe = AddUniverse(altCoarse => { // Keep names with positive sentiment in both the report and MD&A sections. - var alt = from d in altCoarse.OfType() - where d.ReportSentiment.Sentiment > 0m - && d.ManagementDiscussionAnalyasisOfFinancialConditionAndResultsOfOperations.Sentiment > 0m - select d.Symbol; - return _fundamental.Intersect(alt); + var alt = altCoarse.OfType() + .Where(d => d.ReportSentiment != null && d.ReportSentiment.Sentiment > 0m + && d.ManagementDiscussionAnalyasisOfFinancialConditionAndResultsOfOperations != null + && d.ManagementDiscussionAnalyasisOfFinancialConditionAndResultsOfOperations.Sentiment > 0m) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance); @@ -43,11 +48,11 @@ where d.ReportSentiment.Sentiment > 0m private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-brainsentimentindicatoruniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-brainsentimentindicatoruniverse/Main.cs index b2fc06a8c5..d34df220d9 100644 --- a/project-templates/csharp/alternative-data-chain-universe-brainsentimentindicatoruniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-brainsentimentindicatoruniverse/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class BrainSentimentIndicatorChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = []; + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -19,22 +19,26 @@ public override void Initialize() SetCash(100000); Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: positive 7-day media sentiment with active mention coverage, intersected with the fundamental list. + // Second universe: positive 7-day Brain sentiment, ranked by dollar volume. _universe = AddUniverse(altCoarse => { - // Keep names with both active mention coverage and positive 7-day sentiment. - var alt = from d in altCoarse.OfType() - where d.TotalArticleMentions7Days > 0m && d.Sentiment7Days > 0m - select d.Symbol; - return _fundamental.Intersect(alt); + // Keep names with both active 7-day mention coverage and positive 7-day sentiment. + var alt = altCoarse.OfType() + .Where(d => d.TotalArticleMentions7Days > 0m && d.Sentiment7Days > 0m) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance); @@ -42,11 +46,11 @@ where d.TotalArticleMentions7Days > 0m && d.Sentiment7Days > 0m private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-brainstockrankinguniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-brainstockrankinguniverse/Main.cs index db9bb753c1..f06348b50d 100644 --- a/project-templates/csharp/alternative-data-chain-universe-brainstockrankinguniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-brainstockrankinguniverse/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class BrainStockRankingChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = []; + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -19,22 +19,26 @@ public override void Initialize() SetCash(100000); Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe selects top 100 US Equities by dollar volume. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe filters for positive Brain ML rankings across all horizons. + // Second universe: positive Brain ML rankings across 2-, 3-, and 5-day horizons, ranked by dollar volume. _universe = AddUniverse(altCoarse => { - // Keep symbols with positive rankings across 2-day, 3-day, and 5-day horizons. - var alt = from d in altCoarse.OfType() - where d.Rank2Days > 0m && d.Rank3Days > 0m && d.Rank5Days > 0m - select d.Symbol; - return _fundamental.Intersect(alt); + // Keep names with consistent positive momentum across all three horizons. + var alt = altCoarse.OfType() + .Where(d => d.Rank2Days > 0m && d.Rank3Days > 0m && d.Rank5Days > 0m) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance); @@ -42,11 +46,11 @@ where d.Rank2Days > 0m && d.Rank3Days > 0m && d.Rank5Days > 0m private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingdividends/Main.cs b/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingdividends/Main.cs index 1c11533f6b..7a6bc2767e 100644 --- a/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingdividends/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingdividends/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class EODHDUpcomingDividendsChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = []; + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -19,22 +19,26 @@ public override void Initialize() SetCash(100000); Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: ex-dividend in the next day with a $0.05+ payout, intersected with the fundamental list. + // Second universe: ex-dividend in the next day with a $0.05+ payout, ranked by dollar volume. _universe = AddUniverse(altCoarse => { - // Filter symbols with dividends over $0.05 paying within one day. - var alt = from d in altCoarse.OfType() - where d.DividendDate <= Time.AddDays(1) && d.Dividend > 0.05m - select d.Symbol; - return _fundamental.Intersect(alt); + // Keep names with a dividend over $0.05 paying within one day. + var alt = altCoarse.OfType() + .Where(d => d.DividendDate <= Time.AddDays(1) && d.Dividend > 0.05m) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance); @@ -42,11 +46,11 @@ orderby c.DollarVolume descending private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingearnings/Main.cs b/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingearnings/Main.cs index dec76e6e88..a2e2e08461 100644 --- a/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingearnings/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingearnings/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class EODHDUpcomingEarningsChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -20,22 +20,26 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: earnings in the next 3 days with a positive estimate, intersected with the fundamental list. + // Second universe: upcoming earnings with positive estimates, ranked by dollar volume. _universe = AddUniverse(altCoarse => { // Keep names with a positive analyst estimate ahead of the report. - var alt = from d in altCoarse.OfType() - where d.ReportDate <= Time.AddDays(3) && d.Estimate > 0m - select d.Symbol; - return _fundamental.Intersect(alt); + var alt = altCoarse.OfType() + .Where(d => d.ReportDate <= Time.AddDays(3) && d.Estimate > 0m) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -44,12 +48,12 @@ orderby c.DollarVolume descending private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingipos/Main.cs b/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingipos/Main.cs index c736826028..3a451fd669 100644 --- a/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingipos/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingipos/Main.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using QuantConnect.Algorithm.Framework.Portfolio; @@ -11,7 +12,10 @@ public class EODHDUpcomingIPOsChainedUniverseAlgorithm : QCAlgorithm { private static readonly HashSet _dealTypesWanted = new() { EODHD.DealType.Expected, EODHD.DealType.Priced }; - private List _fundamental = new(); + private List _fundamental = []; + // Map of IPO symbol -> IPO date, captured while the event is upcoming so we can + // trade the name once Morningstar has a few days of fundamentals on it. + private Dictionary _ipoDates = new(); private Universe _universe; public override void Initialize() @@ -22,25 +26,47 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: confirmed non-penny upcoming IPOs, intersected with the fundamental list. + // Second universe: trade IPOs one week after listing so fundamentals are populated. _universe = AddUniverse(altCoarse => { - // Keep expected/priced IPOs with a confirmed date and an above-$1 price band. - var alt = from d in altCoarse.OfType() - where _dealTypesWanted.Contains(d.DealType) - && d.IpoDate.HasValue - && (new[] { d.LowestPrice, d.HighestPrice, d.OfferPrice } - .Where(x => x.HasValue).Min() ?? 0m) > 1m - select d.Symbol; - return _fundamental.Intersect(alt); + // Capture expected/priced IPOs with a confirmed date and a >$1 minimum price band. + foreach (var d in altCoarse.OfType()) + { + if (!_dealTypesWanted.Contains(d.DealType) || !d.IpoDate.HasValue) + { + continue; + } + var prices = new[] { d.LowestPrice, d.HighestPrice, d.OfferPrice } + .Where(x => x.HasValue) + .Select(x => x.Value) + .ToList(); + if (prices.Count == 0 || prices.Min() <= 1m) + { + continue; + } + _ipoDates[d.Symbol] = d.IpoDate.Value; + } + // Drop entries whose IPO was more than 30 days ago to keep the dict bounded. + _ipoDates = _ipoDates + .Where(kv => kv.Value > Time.AddDays(-30)) + .ToDictionary(kv => kv.Key, kv => kv.Value); + // Trade IPOs that listed at least 7 days ago, ranked by dollar volume. + var alt = _ipoDates + .Where(kv => kv.Value <= Time.AddDays(-7)) + .Select(kv => kv.Key) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -49,7 +75,7 @@ where _dealTypesWanted.Contains(d.DealType) private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } diff --git a/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingsplits/Main.cs b/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingsplits/Main.cs index 37756bd368..4f6ee36c18 100644 --- a/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingsplits/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-eodhdupcomingsplits/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class EODHDUpcomingSplitsChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -20,22 +20,26 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: forward stock split in the next 3 days, intersected with the fundamental list. + // Second universe: forward stock split in the next 3 days, ranked by dollar volume. _universe = AddUniverse(altCoarse => { // Keep names with a forward split (factor > 1) within 3 days. - var alt = from d in altCoarse.OfType() - where d.SplitDate <= Time.AddDays(3) && d.SplitFactor > 1m - select d.Symbol; - return _fundamental.Intersect(alt); + var alt = altCoarse.OfType() + .Where(d => d.SplitDate <= Time.AddDays(3) && d.SplitFactor > 1m) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -44,12 +48,12 @@ orderby c.DollarVolume descending private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-quivercnbcsuniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-quivercnbcsuniverse/Main.cs index 1af7eaeadf..0284efd98f 100644 --- a/project-templates/csharp/alternative-data-chain-universe-quivercnbcsuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-quivercnbcsuniverse/Main.cs @@ -10,7 +10,7 @@ namespace QuantConnect.Algorithm.CSharp { public class QuiverCNBCsChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -24,19 +24,24 @@ public override void Initialize() // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); // Second universe: 3+ BUY CNBC opinions, intersected with the fundamental list. _universe = AddUniverse(altCoarse => { - // Group raw CNBC opinions by ticker and keep names with 3+ BUY recommendations. - var alt = from g in altCoarse.OfType().GroupBy(x => x.Symbol) - where g.Count(x => x.Direction == OrderDirection.Buy) >= 3 - select g.Key; - return _fundamental.Intersect(alt); + // Group raw CNBC opinions by ticker and keep names with 2+ BUY recommendations. + var alt = altCoarse.OfType() + .GroupBy(x => x.Symbol) + .Where(g => g.Count(x => x.Direction == OrderDirection.Buy) >= 2) + .Select(g => g.Key) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -45,12 +50,12 @@ where g.Count(x => x.Direction == OrderDirection.Buy) >= 3 private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-quivergovernmentcontractuniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-quivergovernmentcontractuniverse/Main.cs index 97f349f94f..f85c7600c3 100644 --- a/project-templates/csharp/alternative-data-chain-universe-quivergovernmentcontractuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-quivergovernmentcontractuniverse/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class QuiverGovernmentContractChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -20,22 +20,27 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: 3+ government contracts totalling over $50K, intersected with the fundamental list. + // Second universe: 3+ government contracts totalling over $50K, ranked by dollar volume. _universe = AddUniverse(altCoarse => { // Group by ticker and keep names with 3+ contracts totalling over $50K. - var alt = from g in altCoarse.OfType().GroupBy(x => x.Symbol) - where g.Count() >= 3 && g.Sum(x => x.Amount) > 50000m - select g.Key; - return _fundamental.Intersect(alt); + var alt = altCoarse.OfType() + .GroupBy(x => x.Symbol) + .Where(g => g.Count() >= 3 && g.Sum(x => x.Amount) > 50000m) + .Select(g => g.Key) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -44,7 +49,7 @@ where g.Count() >= 3 && g.Sum(x => x.Amount) > 50000m private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } diff --git a/project-templates/csharp/alternative-data-chain-universe-quiverinsidertradinguniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-quiverinsidertradinguniverse/Main.cs index a0e2657f52..e639871dd1 100644 --- a/project-templates/csharp/alternative-data-chain-universe-quiverinsidertradinguniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-quiverinsidertradinguniverse/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class QuiverInsiderTradingChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -20,36 +20,30 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: 10 largest insider-trading dollar volumes, intersected with the fundamental list. + // Second universe: 10 largest insider-trading dollar volumes, ranked by dollar volume. _universe = AddUniverse(altCoarse => { // Aggregate insider dollar volume per ticker and keep the 10 largest. - var dollarVolume = new Dictionary(); - foreach (var d in altCoarse.OfType()) - { - if (d.PricePerShare == null || d.PricePerShare == 0m) - { - continue; - } - if (!dollarVolume.ContainsKey(d.Symbol)) - { - dollarVolume[d.Symbol] = 0m; - } - dollarVolume[d.Symbol] += (d.Shares ?? 0m) * d.PricePerShare.Value; - } - var alt = dollarVolume - .OrderByDescending(kvp => kvp.Value) + var alt = altCoarse.OfType() + .Where(d => d.PricePerShare.HasValue && d.PricePerShare.Value != 0m) + .GroupBy(d => d.Symbol) + .Select(g => new { Symbol = g.Key, DollarVolume = g.Sum(d => (d.Shares ?? 0m) * d.PricePerShare.Value) }) + .OrderByDescending(x => x.DollarVolume) .Take(10) - .Select(kvp => kvp.Key); - return _fundamental.Intersect(alt); + .Select(x => x.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -58,7 +52,7 @@ orderby c.DollarVolume descending private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } diff --git a/project-templates/csharp/alternative-data-chain-universe-quiverlobbyinguniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-quiverlobbyinguniverse/Main.cs index 900b7dd171..bdfb2f6431 100644 --- a/project-templates/csharp/alternative-data-chain-universe-quiverlobbyinguniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-quiverlobbyinguniverse/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class QuiverLobbyingChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -20,22 +20,27 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: $100K+ corporate lobbying spend, intersected with the fundamental list. + // Second universe: $100K+ corporate lobbying spend, ranked by dollar volume. _universe = AddUniverse("QuiverLobbyingUniverse", Resolution.Daily, altCoarse => { // Aggregate lobbying spend per ticker and keep names spending $100K+. - var alt = from g in altCoarse.OfType().GroupBy(x => x.Symbol) - where g.Sum(x => x.Amount) >= 100000m - select g.Key; - return _fundamental.Intersect(alt); + var alt = altCoarse.OfType() + .GroupBy(x => x.Symbol) + .Where(g => g.Sum(x => x.Amount) >= 100000m) + .Select(g => g.Key) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -44,12 +49,12 @@ where g.Sum(x => x.Amount) >= 100000m private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-quiverquantcongressuniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-quiverquantcongressuniverse/Main.cs index 50ca06d58f..113f922e9c 100644 --- a/project-templates/csharp/alternative-data-chain-universe-quiverquantcongressuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-quiverquantcongressuniverse/Main.cs @@ -10,7 +10,7 @@ namespace QuantConnect.Algorithm.CSharp { public class QuiverQuantCongressChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -21,22 +21,26 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: US Congress BUY disclosures over $200K, intersected with the fundamental list. + // Second universe: US Congress BUY disclosures over $200K, ranked by dollar volume. _universe = AddUniverse(altCoarse => { // Keep buy disclosures over $200K to filter out small reports. - var alt = from d in altCoarse.OfType() - where d.Amount > 200000m && d.Transaction == OrderDirection.Buy - select d.Symbol; - return _fundamental.Intersect(alt); + var alt = altCoarse.OfType() + .Where(d => d.Amount > 200000m && d.Transaction == OrderDirection.Buy) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -45,7 +49,7 @@ orderby c.DollarVolume descending private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } diff --git a/project-templates/csharp/alternative-data-chain-universe-smartinsiderintentionuniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-smartinsiderintentionuniverse/Main.cs index fb8eb2e6f7..c84e57f745 100644 --- a/project-templates/csharp/alternative-data-chain-universe-smartinsiderintentionuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-smartinsiderintentionuniverse/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class SmartInsiderIntentionChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -20,22 +20,28 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: $100M+ market-cap buyback intentions over 0.5%, intersected with the fundamental list. + // Second universe: $100M+ market-cap buyback intentions over 0.5%, ranked by dollar volume. _universe = AddUniverse(altCoarse => { // Keep $100M+ market-cap names announcing a buyback over 0.5% of shares. - var alt = from d in altCoarse.OfType() - where d.Percentage > 0.005m && d.USDMarketCap > 100000000m - select d.Symbol; - return _fundamental.Intersect(alt); + // Keep names announcing a buyback over 0.5% of shares. (USDMarketCap is not + // populated for intention records, unlike SmartInsiderTransactionUniverse.) + var alt = altCoarse.OfType() + .Where(d => d.Percentage > 0.005m) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -44,12 +50,12 @@ where d.Percentage > 0.005m && d.USDMarketCap > 100000000m private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-chain-universe-smartinsidertransactionuniverse/Main.cs b/project-templates/csharp/alternative-data-chain-universe-smartinsidertransactionuniverse/Main.cs index c2ed324ccd..fc29d70c57 100644 --- a/project-templates/csharp/alternative-data-chain-universe-smartinsidertransactionuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-chain-universe-smartinsidertransactionuniverse/Main.cs @@ -9,7 +9,7 @@ namespace QuantConnect.Algorithm.CSharp { public class SmartInsiderTransactionChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = new(); + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -20,22 +20,26 @@ public override void Initialize() Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Minute; - // First universe: top 100 US Equities by dollar volume; emits Universe.Unchanged. + // First universe: store all US Equity fundamentals; emits Universe.Unchanged. AddUniverse(fundamental => { - _fundamental = (from c in fundamental - orderby c.DollarVolume descending - select c.Symbol).Take(100).ToList(); + _fundamental = [..fundamental]; return Universe.Unchanged; }); - // Second universe: $100M+ market-cap buybacks over 0.5%, intersected with the fundamental list. + // Second universe: $100M+ market-cap buybacks over 0.5%, ranked by dollar volume. _universe = AddUniverse(altCoarse => { // Keep $100M+ market-cap names buying back over 0.5% of shares. - var alt = from d in altCoarse.OfType() - where d.BuybackPercentage > 0.005m && d.USDMarketCap > 100000000m - select d.Symbol; - return _fundamental.Intersect(alt); + var alt = altCoarse.OfType() + .Where(d => d.BuybackPercentage > 0.005m && d.USDMarketCap > 100000000m) + .Select(d => d.Symbol) + .ToHashSet(); + Plot("Universe", "Raw", alt.Count); + return _fundamental + .Where(c => alt.Contains(c.Symbol)) + .OrderByDescending(c => c.DollarVolume) + .Select(c => c.Symbol) + .Take(100); }); // Rebalance before market open to trade today's intersection. @@ -44,12 +48,12 @@ where d.BuybackPercentage > 0.005m && d.USDMarketCap > 100000000m private void Rebalance() { - if (_universe.Selected == null || _universe.Selected.Count == 0) + if (_universe.Selected.Count == 0) { return; } - var weight = 1m / _universe.Selected.Count; + var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingipos/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingipos/Main.cs index 502076b611..59906874e1 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingipos/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingipos/Main.cs @@ -24,11 +24,14 @@ public override void Initialize() { // Keep expected/priced IPOs with a confirmed date and an above-$1 price band. return from d in data.OfType() + let prices = new[] { d.LowestPrice, d.HighestPrice, d.OfferPrice } + .Where(x => x.HasValue) + .Select(x => x.Value) + .ToList() where _dealTypesWanted.Contains(d.DealType) && d.IpoDate.HasValue - && new[] { d.LowestPrice, d.HighestPrice, d.OfferPrice } - .Where(x => x.HasValue) - .Min().Value > 1m + && prices.Count > 0 + && prices.Min() > 1m select d.Symbol; }); diff --git a/project-templates/csharp/alternative-data-universe-quivercnbcsuniverse/Main.cs b/project-templates/csharp/alternative-data-universe-quivercnbcsuniverse/Main.cs index e489b1fbf8..0a256ee141 100644 --- a/project-templates/csharp/alternative-data-universe-quivercnbcsuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-universe-quivercnbcsuniverse/Main.cs @@ -15,6 +15,7 @@ public override void Initialize() SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); + Settings.SeedInitialPrices = true; // Trade daily on CNBC opinion updates. UniverseSettings.Resolution = Resolution.Daily; @@ -39,9 +40,13 @@ private void Rebalance() { return; } - - var weight = 1m / _universe.Selected.Count; - var targets = _universe.Selected + var securities = _universe.Selected.Where(s => Securities[s].Price > 0).ToList(); + if (securities.Count == 0) + { + return; + } + var weight = securities.Count >= 10 ? 1m / securities.Count : 0.1m; + var targets = securities .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-universe-quiverquantcongressuniverse/Main.cs b/project-templates/csharp/alternative-data-universe-quiverquantcongressuniverse/Main.cs index 3792ef252b..cb96403f29 100644 --- a/project-templates/csharp/alternative-data-universe-quiverquantcongressuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-universe-quiverquantcongressuniverse/Main.cs @@ -15,6 +15,7 @@ public override void Initialize() SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); + Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Daily; // Universe of US Equities recently bought by US Congress members in trades over $200K. diff --git a/project-templates/csharp/alternative-data-universe-smartinsiderintentionuniverse/Main.cs b/project-templates/csharp/alternative-data-universe-smartinsiderintentionuniverse/Main.cs index c5959cbb69..6edd6caeb1 100644 --- a/project-templates/csharp/alternative-data-universe-smartinsiderintentionuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-universe-smartinsiderintentionuniverse/Main.cs @@ -14,15 +14,17 @@ public override void Initialize() SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); + Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Daily; - // Universe of large-cap US Equities announcing meaningful buyback intentions. + // Universe of US Equities announcing meaningful buyback intentions. _universe = AddUniverse(data => { - // Keep $100M+ market-cap names announcing a buyback over 0.5% of shares. - return from d in data.OfType() - where d.Percentage > 0.005m && d.USDMarketCap > 100000000m - select d.Symbol; + // Keep names announcing a buyback over 0.5% of shares. (USDMarketCap is not + // populated for intention records, unlike SmartInsiderTransactionUniverse.) + return data.OfType() + .Where(d => d.Percentage > 0.005m) + .Select(d => d.Symbol); }); // Rebalance shortly after the open so today's universe is locked in. @@ -35,9 +37,13 @@ private void Rebalance() { return; } - - var weight = 1m / _universe.Selected.Count; - var targets = _universe.Selected + var securities = _universe.Selected.Where(s => Securities[s].Price > 0).ToList(); + if (securities.Count == 0) + { + return; + } + var weight = securities.Count >= 10 ? 1m / securities.Count : 0.1m; + var targets = securities .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/csharp/alternative-data-universe-smartinsidertransactionuniverse/Main.cs b/project-templates/csharp/alternative-data-universe-smartinsidertransactionuniverse/Main.cs index a29e1337f7..a743dffdb7 100644 --- a/project-templates/csharp/alternative-data-universe-smartinsidertransactionuniverse/Main.cs +++ b/project-templates/csharp/alternative-data-universe-smartinsidertransactionuniverse/Main.cs @@ -14,6 +14,7 @@ public override void Initialize() SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); + Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Daily; // Universe of large-cap US Equities executing meaningful share buybacks. @@ -35,9 +36,13 @@ private void Rebalance() { return; } - - var weight = 1m / _universe.Selected.Count; - var targets = _universe.Selected + var securities = _universe.Selected.Where(s => Securities[s].Price > 0).ToList(); + if (securities.Count == 0) + { + return; + } + var weight = securities.Count >= 10 ? 1m / securities.Count : 0.1m; + var targets = securities .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); diff --git a/project-templates/python/alternative-data-chain-universe-braincompanyfilinglanguagemetricsuniverseall/main.py b/project-templates/python/alternative-data-chain-universe-braincompanyfilinglanguagemetricsuniverseall/main.py index 5e7299e160..1758ae1e84 100644 --- a/project-templates/python/alternative-data-chain-universe-braincompanyfilinglanguagemetricsuniverseall/main.py +++ b/project-templates/python/alternative-data-chain-universe-braincompanyfilinglanguagemetricsuniverseall/main.py @@ -4,7 +4,8 @@ class BrainCompanyFilingLanguageMetricsChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -12,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: positive sentiment in latest SEC filings, intersected with the fundamental list. + # Second universe: positive sentiment in latest SEC filings, ranked by dollar volume. self._universe = self.add_universe(BrainCompanyFilingLanguageMetricsUniverseAll, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -24,22 +25,25 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[BrainCompanyFilingLanguageMetricsUniverseAll]) -> List[Symbol]: # Keep names with positive sentiment in both the report and MD&A sections. - alt = [d.symbol for d in alt_coarse - if d.report_sentiment and d.report_sentiment.sentiment and d.report_sentiment.sentiment > 0 and - d.management_discussion_analyasis_of_financial_condition_and_results_of_operations and - d.management_discussion_analyasis_of_financial_condition_and_results_of_operations.sentiment and - d.management_discussion_analyasis_of_financial_condition_and_results_of_operations.sentiment > 0] - return [s for s in self._fundamental if s in alt] + alt = {d.symbol for d in alt_coarse + if d.report_sentiment and d.report_sentiment.sentiment and d.report_sentiment.sentiment > 0 + and d.management_discussion_analyasis_of_financial_condition_and_results_of_operations + and d.management_discussion_analyasis_of_financial_condition_and_results_of_operations.sentiment + and d.management_discussion_analyasis_of_financial_condition_and_results_of_operations.sentiment > 0} + self.plot('Universe', 'Raw', len(alt)) + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-brainsentimentindicatoruniverse/main.py b/project-templates/python/alternative-data-chain-universe-brainsentimentindicatoruniverse/main.py index 0e1d188e7d..e9fdb230eb 100644 --- a/project-templates/python/alternative-data-chain-universe-brainsentimentindicatoruniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-brainsentimentindicatoruniverse/main.py @@ -4,7 +4,8 @@ class BrainSentimentIndicatorChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -12,11 +13,11 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # Add a fundamental universe to track the most liquid US Equities by dollar volume. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Add a Brain Sentiment universe, restricted to high-sentiment names within the fundamental list. + # Second universe: positive 7-day Brain sentiment, ranked by dollar volume. self._universe = self.add_universe(BrainSentimentIndicatorUniverse, self._select_assets) - # Rebalance before market open. + # Rebalance before market open to trade today's intersection. self.schedule.on( self.date_rules.every_day("SPY"), self.time_rules.at(9, 0), @@ -24,22 +25,23 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - self._fundamental = [c.symbol for c in sorted(fundamental, key=lambda x: x.dollar_volume)[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[BrainSentimentIndicatorUniverse]) -> List[Symbol]: - # Keep only names with active mention coverage and positive sentiment. - alt = [d.symbol for d in alt_coarse - if d.total_article_mentions_7_days and - d.total_article_mentions_7_days > 0 and - d.sentiment_7_days and - d.sentiment_7_days > 0] - return [s for s in self._fundamental if s in alt] + # Keep names with both active 7-day mention coverage and positive 7-day sentiment. + alt = {d.symbol for d in alt_coarse + if d.total_article_mentions_7_days and d.total_article_mentions_7_days > 0 + and d.sentiment_7_days and d.sentiment_7_days > 0} + self.plot('Universe', 'Raw', len(alt)) + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - # Enter the universe equally weighted across all selected assets. - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py b/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py index afc7c1982a..ef47895a53 100644 --- a/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py @@ -5,7 +5,7 @@ class BrainStockRankingChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: positive Brain ML rankings across 2-, 3-, and 5-day horizons, intersected with the fundamental list. + # Second universe: positive Brain ML rankings across 2-, 3-, and 5-day horizons, ranked by dollar volume. self._universe = self.add_universe(BrainStockRankingUniverse, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,21 +25,24 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[BrainStockRankingUniverse]) -> List[Symbol]: # Keep names with consistent positive momentum across all three horizons. - alt = [d.symbol for d in alt_coarse - if d.rank_2_days and d.rank_2_days > 0 and - d.rank_3_days and d.rank_3_days > 0 and - d.rank_5_days and d.rank_5_days > 0] - return [s for s in self._fundamental if s in alt] + alt = {d.symbol for d in alt_coarse + if d.rank_2_days and d.rank_2_days > 0 + and d.rank_3_days and d.rank_3_days > 0 + and d.rank_5_days and d.rank_5_days > 0} + self.plot('Universe', 'Raw', len(alt)) + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py b/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py index 218fb5a1a8..dfc173e3d2 100644 --- a/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py +++ b/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py @@ -5,7 +5,7 @@ class EODHDUpcomingDividendsChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: ex-dividend in the next day with a $0.05+ payout, intersected with the fundamental list. + # Second universe: ex-dividend in the next day with a $0.05+ payout, ranked by dollar volume. self._universe = self.add_universe(EODHDUpcomingDividends, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,20 +25,23 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[EODHDUpcomingDividends]) -> List[Symbol]: # Keep names with a dividend over $0.05 paying within one day. - alt = [d.symbol for d in alt_coarse - if d.dividend_date and d.dividend and - d.dividend_date <= self.time + timedelta(1) and d.dividend > 0.05] - return [s for s in self._fundamental if s in alt] + alt = {d.symbol for d in alt_coarse + if d.dividend_date and d.dividend + and d.dividend_date <= self.time + timedelta(1) and d.dividend > 0.05} + self.plot('Universe', 'Raw', len(alt)) + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-eodhdupcomingearnings/main.py b/project-templates/python/alternative-data-chain-universe-eodhdupcomingearnings/main.py index fead4fc361..87502a4689 100644 --- a/project-templates/python/alternative-data-chain-universe-eodhdupcomingearnings/main.py +++ b/project-templates/python/alternative-data-chain-universe-eodhdupcomingearnings/main.py @@ -5,7 +5,7 @@ class EODHDUpcomingEarningsChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: earnings in the next 3 days with a positive estimate, intersected with the fundamental list. + # Second universe: upcoming earnings with positive estimates, ranked by dollar volume. self._universe = self.add_universe(EODHDUpcomingEarnings, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,20 +25,24 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[EODHDUpcomingEarnings]) -> List[Symbol]: # Keep names with a positive analyst estimate ahead of the report. - alt = [d.symbol for d in alt_coarse + alt = {d.symbol for d in alt_coarse if d.report_date and d.estimate and - d.report_date <= self.time + timedelta(3) and d.estimate > 0] - return [s for s in self._fundamental if s in alt] + d.report_date <= self.time + timedelta(3) and d.estimate > 0} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-eodhdupcomingipos/main.py b/project-templates/python/alternative-data-chain-universe-eodhdupcomingipos/main.py index c12d8e58e4..439f3e43be 100644 --- a/project-templates/python/alternative-data-chain-universe-eodhdupcomingipos/main.py +++ b/project-templates/python/alternative-data-chain-universe-eodhdupcomingipos/main.py @@ -5,7 +5,10 @@ class EODHDUpcomingIPOsChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] + # Map of IPO symbol -> IPO date, captured while the event is upcoming so we can + # trade the name once Morningstar has a few days of fundamentals on it. + _ipo_dates: Dict[Symbol, datetime] = {} def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +16,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: confirmed non-penny upcoming IPOs, intersected with the fundamental list. + # Second universe: trade IPOs one week after listing so fundamentals are populated. self._universe = self.add_universe(EODHDUpcomingIPOs, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,17 +28,27 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[EODHDUpcomingIPOs]) -> List[Symbol]: - # Keep expected/priced IPOs with a confirmed date and a >$1 minimum across the price band. - alt = [d.symbol for d in alt_coarse - if d.ipo_date and d.deal_type in [EODHD.DealType.EXPECTED, EODHD.DealType.PRICED] and - (prices := [x for x in [d.lowest_price, d.highest_price, d.offer_price] if x]) and - min(prices) > 1] - return [s for s in self._fundamental if s in alt] + # Capture expected/priced IPOs with a confirmed date and a >$1 minimum price band. + for d in alt_coarse: + if (d.ipo_date and d.deal_type in [EODHD.DealType.EXPECTED, EODHD.DealType.PRICED] and + (prices := [x for x in [d.lowest_price, d.highest_price, d.offer_price] if x]) and + min(prices) > 1): + self._ipo_dates[d.symbol] = d.ipo_date + # Drop entries whose IPO was more than 30 days ago to keep the dict bounded. + self._ipo_dates = {s: d for s, d in self._ipo_dates.items() + if d > self.time - timedelta(30)} + # Trade IPOs that listed at least 7 days ago, ranked by dollar volume. + alt = {s for s, d in self._ipo_dates.items() + if d <= self.time - timedelta(7)} + self.plot('Universe', 'Raw', len(alt)) + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: diff --git a/project-templates/python/alternative-data-chain-universe-eodhdupcomingsplits/main.py b/project-templates/python/alternative-data-chain-universe-eodhdupcomingsplits/main.py index 38050f38a3..f3cc18ed58 100644 --- a/project-templates/python/alternative-data-chain-universe-eodhdupcomingsplits/main.py +++ b/project-templates/python/alternative-data-chain-universe-eodhdupcomingsplits/main.py @@ -5,7 +5,7 @@ class EODHDUpcomingSplitsChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: forward stock split in the next 3 days, intersected with the fundamental list. + # Second universe: forward stock split in the next 3 days, ranked by dollar volume. self._universe = self.add_universe(EODHDUpcomingSplits, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,20 +25,24 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[EODHDUpcomingSplits]) -> List[Symbol]: # Keep names with a forward split (factor > 1) within 3 days. - alt = [d.symbol for d in alt_coarse + alt = {d.symbol for d in alt_coarse if d.split_date and d.split_factor and - d.split_date <= self.time + timedelta(3) and d.split_factor > 1] - return [s for s in self._fundamental if s in alt] + d.split_date <= self.time + timedelta(3) and d.split_factor > 1} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-quivercnbcsuniverse/main.py b/project-templates/python/alternative-data-chain-universe-quivercnbcsuniverse/main.py index e5cc991dd2..e38c9de9f0 100644 --- a/project-templates/python/alternative-data-chain-universe-quivercnbcsuniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-quivercnbcsuniverse/main.py @@ -5,7 +5,7 @@ class QuiverCNBCsChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: 3+ BUY CNBC opinions, intersected with the fundamental list. + # Second universe: CNBC opinions intersected with fundamentals, ranked by dollar volume. self._universe = self.add_universe(QuiverCNBCsUniverse, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,22 +25,26 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]: - # Group raw CNBC opinions by ticker and keep names with 3+ BUY recommendations. + # Group raw CNBC opinions by ticker and keep names with 2+ BUY recommendations. cnbc_by_symbol: dict[Symbol, list[QuiverCNBCsUniverse]] = {} for d in alt_coarse: cnbc_by_symbol.setdefault(d.symbol, []).append(d) - alt = [s for s, ds in cnbc_by_symbol.items() - if sum(1 for d in ds if d.direction == OrderDirection.BUY) >= 3] - return [s for s in self._fundamental if s in alt] + alt = {s for s, ds in cnbc_by_symbol.items() + if sum(1 for d in ds if d.direction == OrderDirection.BUY) >= 2} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-quivergovernmentcontractuniverse/main.py b/project-templates/python/alternative-data-chain-universe-quivergovernmentcontractuniverse/main.py index 67ea29bbdd..5c9ef63e59 100644 --- a/project-templates/python/alternative-data-chain-universe-quivergovernmentcontractuniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-quivergovernmentcontractuniverse/main.py @@ -5,7 +5,7 @@ class QuiverGovernmentContractChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: 3+ government contracts totalling over $50K, intersected with the fundamental list. + # Second universe: 3+ government contracts totalling over $50K, ranked by dollar volume. self._universe = self.add_universe(QuiverGovernmentContractUniverse, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,8 +25,7 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[QuiverGovernmentContractUniverse]) -> List[Symbol]: @@ -34,9 +33,14 @@ def _select_assets(self, alt_coarse: List[QuiverGovernmentContractUniverse]) -> contracts_by_symbol: dict[Symbol, list[QuiverGovernmentContractUniverse]] = {} for d in alt_coarse: contracts_by_symbol.setdefault(d.symbol, []).append(d) - alt = [s for s, ds in contracts_by_symbol.items() - if len(ds) >= 3 and sum(x.amount or 0 for x in ds) > 50000] - return [s for s in self._fundamental if s in alt] + alt = {s for s, ds in contracts_by_symbol.items() + if len(ds) >= 3 and sum(x.amount or 0 for x in ds) > 50000} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: diff --git a/project-templates/python/alternative-data-chain-universe-quiverinsidertradinguniverse/main.py b/project-templates/python/alternative-data-chain-universe-quiverinsidertradinguniverse/main.py index 165e71f012..bdcc7c453c 100644 --- a/project-templates/python/alternative-data-chain-universe-quiverinsidertradinguniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-quiverinsidertradinguniverse/main.py @@ -5,7 +5,7 @@ class QuiverInsiderTradingChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: 10 largest insider-trading dollar volumes, intersected with the fundamental list. + # Second universe: 10 largest insider-trading dollar volumes, ranked by dollar volume. self._universe = self.add_universe(QuiverInsiderTradingUniverse, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,8 +25,7 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[QuiverInsiderTradingUniverse]) -> List[Symbol]: @@ -36,8 +35,13 @@ def _select_assets(self, alt_coarse: List[QuiverInsiderTradingUniverse]) -> List if not d.price_per_share: continue dollar_volume[d.symbol] = dollar_volume.get(d.symbol, 0) + (d.shares or 0) * d.price_per_share - alt = [s for s, _ in sorted(dollar_volume.items(), key=lambda kv: kv[1])[-10:]] - return [s for s in self._fundamental if s in alt] + alt = {s for s, _ in sorted(dollar_volume.items(), key=lambda kv: kv[1])[-10:]} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: diff --git a/project-templates/python/alternative-data-chain-universe-quiverlobbyinguniverse/main.py b/project-templates/python/alternative-data-chain-universe-quiverlobbyinguniverse/main.py index c441c99aa9..9c6291ccd3 100644 --- a/project-templates/python/alternative-data-chain-universe-quiverlobbyinguniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-quiverlobbyinguniverse/main.py @@ -5,7 +5,7 @@ class QuiverLobbyingChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: $100K+ corporate lobbying spend, intersected with the fundamental list. + # Second universe: $100K+ corporate lobbying spend, ranked by dollar volume. self._universe = self.add_universe(QuiverLobbyingUniverse, "QuiverLobbyingUniverse", Resolution.DAILY, self._select_assets) # Rebalance before market open to trade today's intersection. @@ -26,8 +26,7 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[QuiverLobbyingUniverse]) -> List[Symbol]: @@ -35,12 +34,17 @@ def _select_assets(self, alt_coarse: List[QuiverLobbyingUniverse]) -> List[Symbo spend_by_symbol: dict[Symbol, float] = {} for d in alt_coarse: spend_by_symbol[d.symbol] = spend_by_symbol.get(d.symbol, 0) + (d.amount or 0) - alt = [s for s, v in spend_by_symbol.items() if v >= 100000] - return [s for s in self._fundamental if s in alt] + alt = {s for s, v in spend_by_symbol.items() if v >= 100000} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-quiverquantcongressuniverse/main.py b/project-templates/python/alternative-data-chain-universe-quiverquantcongressuniverse/main.py index ab8f41b25f..95f00b6678 100644 --- a/project-templates/python/alternative-data-chain-universe-quiverquantcongressuniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-quiverquantcongressuniverse/main.py @@ -5,7 +5,7 @@ class QuiverQuantCongressChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: US Congress BUY disclosures over $200K, intersected with the fundamental list. + # Second universe: US Congress BUY disclosures over $200K, ranked by dollar volume. self._universe = self.add_universe(QuiverQuantCongressUniverse, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,16 +25,20 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[QuiverQuantCongressUniverse]) -> List[Symbol]: # Keep buy disclosures over $200K to filter out small reports. - alt = [d.symbol for d in alt_coarse + alt = {d.symbol for d in alt_coarse if d.amount and d.amount > 200000 and - d.transaction == OrderDirection.BUY] - return [s for s in self._fundamental if s in alt] + d.transaction == OrderDirection.BUY} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: diff --git a/project-templates/python/alternative-data-chain-universe-smartinsiderintentionuniverse/main.py b/project-templates/python/alternative-data-chain-universe-smartinsiderintentionuniverse/main.py index d2dbfb13ae..f981041b68 100644 --- a/project-templates/python/alternative-data-chain-universe-smartinsiderintentionuniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-smartinsiderintentionuniverse/main.py @@ -5,7 +5,7 @@ class SmartInsiderIntentionChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: $100M+ market-cap buyback intentions over 0.5%, intersected with the fundamental list. + # Second universe: $100M+ market-cap buyback intentions over 0.5%, ranked by dollar volume. self._universe = self.add_universe(SmartInsiderIntentionUniverse, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,20 +25,24 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[SmartInsiderIntentionUniverse]) -> List[Symbol]: - # Keep $100M+ market-cap names announcing a buyback over 0.5% of shares. - alt = [d.symbol for d in alt_coarse - if d.percentage and d.usd_market_cap and - d.percentage > 0.005 and d.usd_market_cap > 100000000] - return [s for s in self._fundamental if s in alt] + # Keep names announcing a buyback over 0.5% of shares. (USDMarketCap is not + # populated for intention records, unlike SmartInsiderTransactionUniverse.) + alt = {d.symbol for d in alt_coarse + if d.percentage and d.percentage > 0.005} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-chain-universe-smartinsidertransactionuniverse/main.py b/project-templates/python/alternative-data-chain-universe-smartinsidertransactionuniverse/main.py index 8c70626968..e2575d9623 100644 --- a/project-templates/python/alternative-data-chain-universe-smartinsidertransactionuniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-smartinsidertransactionuniverse/main.py @@ -5,7 +5,7 @@ class SmartInsiderTransactionChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] + _fundamental: List[Fundamental] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -13,9 +13,9 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. + # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Second universe: $100M+ market-cap buybacks over 0.5%, intersected with the fundamental list. + # Second universe: $100M+ market-cap buybacks over 0.5%, ranked by dollar volume. self._universe = self.add_universe(SmartInsiderTransactionUniverse, self._select_assets) # Rebalance before market open to trade today's intersection. self.schedule.on( @@ -25,20 +25,24 @@ def initialize(self) -> None: ) def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume) - self._fundamental = [c.symbol for c in sorted_by_dollar_volume[-100:]] + self._fundamental = fundamental return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[SmartInsiderTransactionUniverse]) -> List[Symbol]: # Keep $100M+ market-cap names buying back over 0.5% of shares. - alt = [d.symbol for d in alt_coarse + alt = {d.symbol for d in alt_coarse if d.buyback_percentage and d.usd_market_cap and - d.buyback_percentage > 0.005 and d.usd_market_cap > 100000000] - return [s for s in self._fundamental if s in alt] + d.buyback_percentage > 0.005 and d.usd_market_cap > 100000000} + self.plot('Universe', 'Raw', len(alt)) + # Among the matches, keep the 100 most liquid by dollar volume. + return [c.symbol for c in sorted( + [c for c in self._fundamental if c.symbol in alt], + key=lambda c: c.dollar_volume, reverse=True + )[:100]] def _rebalance(self) -> None: if not self._universe.selected: return - weight = 1 / len(self._universe.selected) + weight = min(1 / len(self._universe.selected), 0.1) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-universe-eodhdupcomingipos/main.py b/project-templates/python/alternative-data-universe-eodhdupcomingipos/main.py index 51613dd290..38bf9a8061 100644 --- a/project-templates/python/alternative-data-universe-eodhdupcomingipos/main.py +++ b/project-templates/python/alternative-data-universe-eodhdupcomingipos/main.py @@ -1,6 +1,5 @@ # region imports from AlgorithmImports import * -from QuantConnect.DataSource.EODHD import DealType # endregion class EODHDUpcomingIPOsUniverseAlgorithm(QCAlgorithm): @@ -20,7 +19,7 @@ def initialize(self) -> None: def _select_assets(self, data: List[EODHDUpcomingIPOs]) -> List[Symbol]: # Keep expected/priced IPOs with a confirmed date and a >$1 minimum across the price band. return [d.symbol for d in data - if d.ipo_date and d.deal_type in [DealType.EXPECTED, DealType.PRICED] + if d.ipo_date and d.deal_type in [EODHD.DealType.EXPECTED, EODHD.DealType.PRICED] and (prices := [x for x in [d.lowest_price, d.highest_price, d.offer_price] if x]) and min(prices) > 1] diff --git a/project-templates/python/alternative-data-universe-quivercnbcsuniverse/main.py b/project-templates/python/alternative-data-universe-quivercnbcsuniverse/main.py index a3dadebadd..eb5f8d1a43 100644 --- a/project-templates/python/alternative-data-universe-quivercnbcsuniverse/main.py +++ b/project-templates/python/alternative-data-universe-quivercnbcsuniverse/main.py @@ -8,6 +8,7 @@ def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) + self.settings.seed_initial_prices = True # Trade daily on CNBC opinion updates. self.universe_settings.resolution = Resolution.DAILY @@ -29,8 +30,10 @@ def _select_assets(self, data: List[QuiverCNBCsUniverse]) -> List[Symbol]: def _rebalance(self) -> None: if not self._universe.selected: return - - weight = 1 / len(self._universe.selected) - targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] + securities = [s for s in self._universe.selected if self.securities[s].price] + if not securities: + return + weight = min(1 / len(securities), 0.1) + targets = [PortfolioTarget(symbol, weight) for symbol in securities] self.set_holdings(targets, liquidate_existing_holdings=True) diff --git a/project-templates/python/alternative-data-universe-quiverquantcongressuniverse/main.py b/project-templates/python/alternative-data-universe-quiverquantcongressuniverse/main.py index 90109f0be1..9bd9142150 100644 --- a/project-templates/python/alternative-data-universe-quiverquantcongressuniverse/main.py +++ b/project-templates/python/alternative-data-universe-quiverquantcongressuniverse/main.py @@ -8,6 +8,7 @@ def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) + self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.DAILY # Universe of US Equities recently bought by US Congress members in trades over $200K. diff --git a/project-templates/python/alternative-data-universe-smartinsiderintentionuniverse/main.py b/project-templates/python/alternative-data-universe-smartinsiderintentionuniverse/main.py index 0a8a4c585f..ca23551ca1 100644 --- a/project-templates/python/alternative-data-universe-smartinsiderintentionuniverse/main.py +++ b/project-templates/python/alternative-data-universe-smartinsiderintentionuniverse/main.py @@ -8,6 +8,7 @@ def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) + self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.DAILY # Universe of large-cap US Equities announcing meaningful buyback intentions. @@ -17,16 +18,18 @@ def initialize(self) -> None: self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.at(9, 0, 0), self._rebalance) def _select_assets(self, data: List[SmartInsiderIntentionUniverse]) -> List[Symbol]: - # Keep $100M+ market-cap names announcing a buyback over 0.5% of shares. + # Keep names announcing a buyback over 0.5% of shares. (USDMarketCap is not + # populated for intention records, unlike SmartInsiderTransactionUniverse.) return [d.symbol for d in data - if d.percentage and d.usd_market_cap - and d.percentage > 0.005 and d.usd_market_cap > 100000000] + if d.percentage and d.percentage > 0.005] def _rebalance(self) -> None: if not self._universe.selected: return - - weight = 1 / len(self._universe.selected) - targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] + securities = [s for s in self._universe.selected if self.securities[s].price] + if not securities: + return + weight = min(1 / len(securities), 0.1) + targets = [PortfolioTarget(symbol, weight) for symbol in securities] self.set_holdings(targets, liquidate_existing_holdings=True) diff --git a/project-templates/python/alternative-data-universe-smartinsidertransactionuniverse/main.py b/project-templates/python/alternative-data-universe-smartinsidertransactionuniverse/main.py index 62b73f2383..d1e82a4a33 100644 --- a/project-templates/python/alternative-data-universe-smartinsidertransactionuniverse/main.py +++ b/project-templates/python/alternative-data-universe-smartinsidertransactionuniverse/main.py @@ -8,6 +8,7 @@ def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) + self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.DAILY # Universe of large-cap US Equities executing meaningful share buybacks. @@ -25,8 +26,10 @@ def _select_assets(self, data: List[SmartInsiderTransactionUniverse]) -> List[Sy def _rebalance(self) -> None: if not self._universe.selected: return - - weight = 1 / len(self._universe.selected) - targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] + securities = [s for s in self._universe.selected if self.securities[s].price] + if not securities: + return + weight = min(1 / len(securities), 0.1) + targets = [PortfolioTarget(symbol, weight) for symbol in securities] self.set_holdings(targets, liquidate_existing_holdings=True) diff --git a/project-templates/python/custom-indicator/main.py b/project-templates/python/custom-indicator/main.py index fac808ba2f..140da03788 100644 --- a/project-templates/python/custom-indicator/main.py +++ b/project-templates/python/custom-indicator/main.py @@ -9,7 +9,7 @@ def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) # Request daily SPY data to feed the indicators to generate trade signals and trade. - self._spy = self.add_equity("SPY") + self._spy = self.add_equity("SPY", data_normalization_mode=DataNormalizationMode.RAW) # Create a custom money flow index to generate a trade signal. self._custom_mfi = CustomMoneyFlowIndex(20) # Warm up for immediate usage of indicators.