diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs index 078e84a504..5226f4d045 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Data.UniverseSelection; @@ -7,6 +8,7 @@ namespace QuantConnect.Algorithm.CSharp { public class EODHDUpcomingDividendsUniverseAlgorithm : QCAlgorithm { + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -14,19 +16,29 @@ public override void Initialize() SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - - UniverseSettings.Resolution = Resolution.Daily; - // Universe of US Equities going ex-dividend in the next day with a meaningful payout. - _universe = AddUniverse(data => + Settings.SeedInitialPrices = true; + // Add a fundamental universe to track the most liquid US equities by dollar volume. + AddUniverse(fundamental => { - // Keep names with a dividend over $0.05 paying within one day. - return from d in data.OfType() - where d.DividendDate <= Time.AddDays(1) && d.Dividend > 0.05m - select d.Symbol; + // Store the top 50 equities by dollar volume without changing the active universe. + _fundamental = fundamental + .OrderByDescending(f => f.DollarVolume) + .Take(50) + .Select(f => f.Symbol) + .ToList(); + return Universe.Unchanged; }); - - // Rebalance shortly after the open so today's universe is locked in. - Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance); + // Add a dividend universe restricted to high-payout names within the fundamental list. + _universe = AddUniverse( + // Filter for symbols with dividends over $0.05 paying within one day. + data => data + .OfType() + .Where(d => d.DividendDate <= Time.AddDays(1) && d.Dividend > 0.05m) + .Select(d => d.Symbol) + .Where(s => _fundamental.Contains(s)) + ); + // Schedule daily rebalancing at 9:31 AM to trade the current universe selection. + Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 31), Rebalance); } private void Rebalance() @@ -35,13 +47,12 @@ private void Rebalance() { return; } - + // Create an equal weight portfolio with selected securities. var weight = 1m / _universe.Selected.Count; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); - - SetHoldings(targets, liquidateExistingHoldings: true); + SetHoldings(targets, true); } } } diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs index 2737b1b01b..97c903417c 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Linq; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Data.UniverseSelection; @@ -7,6 +8,7 @@ namespace QuantConnect.Algorithm.CSharp { public class EODHDUpcomingEarningsUniverseAlgorithm : QCAlgorithm { + private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -14,19 +16,29 @@ public override void Initialize() SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - - UniverseSettings.Resolution = Resolution.Daily; - // Universe of US Equities reporting earnings in the next 3 days with a positive estimate. - _universe = AddUniverse(data => + Settings.SeedInitialPrices = true; + // Add a fundamental universe to track the most liquid US equities by dollar volume. + AddUniverse(fundamental => { - // Keep names with a positive analyst estimate ahead of the report. - return from d in data.OfType() - where d.ReportDate <= Time.AddDays(3) && d.Estimate > 0m - select d.Symbol; + // Store the top 50 equities by dollar volume without changing the active universe. + _fundamental = fundamental + .OrderByDescending(f => f.DollarVolume) + .Take(50) + .Select(f => f.Symbol) + .ToList(); + return Universe.Unchanged; }); - - // Rebalance shortly after the open so today's universe is locked in. - Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance); + // Add an earnings universe restricted to upcoming reporters within the fundamental list. + _universe = AddUniverse( + // Filter for symbols with a positive analyst estimate reporting within the next 3 days. + data => data + .OfType() + .Where(d => d.ReportDate <= Time.AddDays(3) && d.Estimate > 0m) + .Select(d => d.Symbol) + .Where(s => _fundamental.Contains(s)) + ); + // Schedule daily rebalancing at 9:31 AM to trade the current universe selection. + Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 31), Rebalance); } private void Rebalance() @@ -35,12 +47,11 @@ private void Rebalance() { return; } - + // Create an equal weight portfolio with selected securities. var weight = 1m / _universe.Selected.Count; var targets = _universe.Selected .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); - SetHoldings(targets, liquidateExistingHoldings: true); } } diff --git a/project-templates/python/alternative-data-universe-coingeckouniverse/main.py b/project-templates/python/alternative-data-universe-coingeckouniverse/main.py index 1b826772a8..df3ca58ceb 100644 --- a/project-templates/python/alternative-data-universe-coingeckouniverse/main.py +++ b/project-templates/python/alternative-data-universe-coingeckouniverse/main.py @@ -41,7 +41,7 @@ def _rebalance(self) -> None: if not self._universe.selected: return # Filter for securities with valid prices. - securities = [s for s in self._universe.selected if self.securities[s].price > 0] + securities = [s for s in self._universe.selected if self.securities[s].price] if not securities: return weight = 1 / len(securities) diff --git a/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py b/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py index ed2694dd19..3d805c6512 100644 --- a/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py +++ b/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py @@ -2,31 +2,42 @@ from AlgorithmImports import * # endregion + class EODHDUpcomingDividendsUniverseAlgorithm(QCAlgorithm): + _fundamental: list[Symbol] = [] 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.resolution = Resolution.DAILY - # Universe of US Equities going ex-dividend in the next day with a meaningful payout. + self.set_cash(100_000) + self.settings.seed_initial_prices = True + # Add a fundamental universe to track the most liquid US equities by dollar volume. + self.add_universe(self._fundamental_filter) + # Add a dividend universe restricted to high-payout names within the fundamental list. self._universe = self.add_universe(EODHDUpcomingDividends, self._select_assets) - - # Rebalance shortly after the open so today's universe is locked in. - self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.at(9, 0, 0), self._rebalance) + # Schedule daily rebalancing at 9:31 AM to trade the current universe selection. + self.schedule.on( + self.date_rules.every_day("SPY"), + self.time_rules.at(9, 31), + self._rebalance + ) + + def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: + # Store the top 50 equities by dollar volume without changing the active universe. + self._fundamental = [f.symbol for f in sorted(fundamental, key=lambda f: f.dollar_volume)[-50:]] + return Universe.UNCHANGED def _select_assets(self, data: List[EODHDUpcomingDividends]) -> List[Symbol]: - # Keep names with a dividend over $0.05 paying within one day. - return [d.symbol for d in data - if d.dividend_date and d.dividend - and d.dividend_date <= self.time + timedelta(1) and d.dividend > 0.05] + # Filter for symbols with dividends over $0.05 paying within one day. + alt = [d.symbol for d in data + 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] def _rebalance(self) -> None: if not self._universe.selected: return - + # Create an equal weight portfolio with selected securities. weight = 1 / len(self._universe.selected) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] - - self.set_holdings(targets, liquidate_existing_holdings=True) + self.set_holdings(targets, True) diff --git a/project-templates/python/alternative-data-universe-eodhdupcomingearnings/main.py b/project-templates/python/alternative-data-universe-eodhdupcomingearnings/main.py index 7e7597da05..6ad654993a 100644 --- a/project-templates/python/alternative-data-universe-eodhdupcomingearnings/main.py +++ b/project-templates/python/alternative-data-universe-eodhdupcomingearnings/main.py @@ -2,31 +2,42 @@ from AlgorithmImports import * # endregion + class EODHDUpcomingEarningsUniverseAlgorithm(QCAlgorithm): + _fundamental: list[Symbol] = [] 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.resolution = Resolution.DAILY - # Universe of US Equities reporting earnings in the next 3 days with a positive estimate. + self.set_cash(100_000) + self.settings.seed_initial_prices = True + # Add a fundamental universe to track the most liquid US equities by dollar volume. + self.add_universe(self._fundamental_filter) + # Add an earnings universe restricted to upcoming reporters within the fundamental list. self._universe = self.add_universe(EODHDUpcomingEarnings, self._select_assets) - - # Rebalance shortly after the open so today's universe is locked in. - self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.at(9, 0, 0), self._rebalance) + # Schedule daily rebalancing at 9:00 AM to trade the current universe selection. + self.schedule.on( + self.date_rules.every_day("SPY"), + self.time_rules.at(9, 31), + self._rebalance + ) + + def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: + # Store the top 50 equities by dollar volume without changing the active universe. + self._fundamental = [f.symbol for f in sorted(fundamental, key=lambda f: f.dollar_volume)[-50:]] + return Universe.UNCHANGED def _select_assets(self, data: List[EODHDUpcomingEarnings]) -> List[Symbol]: - # Keep names with a positive analyst estimate ahead of the report. - return [d.symbol for d in data - if d.report_date and d.estimate - and d.report_date <= self.time + timedelta(3) and d.estimate > 0] + # Filter for symbols with a positive analyst estimate reporting within the next 3 days. + alt = [d.symbol for d in data + 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] def _rebalance(self) -> None: if not self._universe.selected: return - + # Create an equal weight portfolio with selected securities. weight = 1 / len(self._universe.selected) targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] - - self.set_holdings(targets, liquidate_existing_holdings=True) + self.set_holdings(targets, True)