From 13584ada639a2a3ff66d501cc2538bc5b415c591 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Mon, 11 May 2026 07:17:13 -0700 Subject: [PATCH 01/24] Refactor comments and improve code formatting --- project-templates/python/ai/main.py | 30 +++++++++++------------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/project-templates/python/ai/main.py b/project-templates/python/ai/main.py index 1a4b80312d..0e7a9cfe70 100644 --- a/project-templates/python/ai/main.py +++ b/project-templates/python/ai/main.py @@ -3,31 +3,30 @@ from AlgorithmImports import * # endregion + class TensorFlowAlgorithm(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) - self.set_cash(100000) + self.set_cash(100_000) + self.settings.seed_initial_prices = True # Request SPY data for model training, prediction and trading. self._spy = self.add_equity("SPY", Resolution.DAILY) - - # Hyperparameter to create the MLP model. + # Hyperparameters to create the MLP model. num_factors = 5 num_neurons_1 = 10 num_neurons_2 = 10 num_neurons_3 = 5 self._epochs = 100 self._learning_rate = 0.0001 - - # Create the MLP model with ReLu activiation. + # Create the MLP model with ReLU activation. self._model = tf.keras.Sequential([ tf.keras.layers.Dense(num_neurons_1, activation=tf.nn.relu, input_shape=(num_factors,)), # input shape required tf.keras.layers.Dense(num_neurons_2, activation=tf.nn.relu), tf.keras.layers.Dense(num_neurons_3, activation=tf.nn.relu), tf.keras.layers.Dense(1) ]) - # 2-year data to train the model. training_length = 500 self._spy.session.size = training_length @@ -35,7 +34,6 @@ def initialize(self) -> None: history = self.history[TradeBar](self._spy, training_length, Resolution.DAILY) for trade_bar in history: self._spy.session.update(trade_bar) - # Train the model to use the prediction right away. self.train(self._my_training_method) # Recalibrate the model weekly to ensure its accuracy on the updated domain. @@ -43,14 +41,12 @@ def initialize(self) -> None: def _get_features_and_labels(self, lookback=5): lookback_series = [] - - # Train and predict the N differencing data, which is more normalized and stationary. + # Train and predict on N-period differencing data which is more normalized and stationary. data = pd.Series([bar.close for bar in self._spy.session][::-1]) for i in range(1, lookback + 1): df = data.diff(i)[lookback:-1] df.name = f"close-{i}" lookback_series.append(df) - X = pd.concat(lookback_series, axis=1).reset_index(drop=True).dropna() Y = data.diff(-1)[lookback:-1].reset_index(drop=True) return X.values, Y.values @@ -58,26 +54,22 @@ def _get_features_and_labels(self, lookback=5): def _my_training_method(self) -> None: # Prepare the processed training data. features, labels = self._get_features_and_labels() - - # Define the loss function, we use MSE in this example + # Define the loss function using MSE for this example. def loss_mse(target_y, predicted_y): return tf.reduce_mean(tf.square(target_y - predicted_y)) - - # Train the model with Adam optimization function. + # Train the model with Adam optimizer. optimizer = tf.keras.optimizers.Adam(learning_rate=self._learning_rate) for i in range(self._epochs): with tf.GradientTape() as t: loss = loss_mse(labels, self._model(features)) - jac = t.gradient(loss, self._model.trainable_weights) optimizer.apply_gradients(zip(jac, self._model.trainable_weights)) def on_data(self, data) -> None: if data.bars: - # Get prediction by the updated features. - new_features, __ = self._get_features_and_labels() + # Get prediction using the updated features. + new_features = self._get_features_and_labels()[0] prediction = self._model(new_features) prediction = float(prediction.numpy()[-1]) - - # If the predicted direction is going upward, buy SPY, else sell. + # If the predicted direction is upward buy SPY, otherwise sell. self.set_holdings(self._spy, 1 if prediction > 0 else -1) From fff3e2465526e8fe7cc1b6d78245f2e978df7dc4 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Mon, 11 May 2026 07:32:24 -0700 Subject: [PATCH 02/24] Update main.py --- .../main.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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..6781107017 100644 --- a/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py @@ -4,7 +4,6 @@ class BrainStockRankingChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] def initialize(self) -> None: @@ -13,11 +12,11 @@ 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. + # Select the top 100 US equities by dollar volume for fundamental filtering. self.add_universe(self._fundamental_filter) - # Second universe: positive Brain ML rankings across 2-, 3-, and 5-day horizons, intersected with the fundamental list. + # Filter for stocks with positive Brain ML rankings across all horizons. self._universe = self.add_universe(BrainStockRankingUniverse, self._select_assets) - # Rebalance before market open to trade today's intersection. + # Rebalance daily before market open to trade the selected universe. self.schedule.on( self.date_rules.every_day("SPY"), self.time_rules.at(9, 0), @@ -30,7 +29,7 @@ def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.Unchan return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[BrainStockRankingUniverse]) -> List[Symbol]: - # Keep names with consistent positive momentum across all three horizons. + # Keep stocks with positive rankings 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 From 27ae8c4aefa93990cbcf56caf7749f6784d340f9 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Mon, 11 May 2026 07:36:19 -0700 Subject: [PATCH 03/24] Update main.py --- .../main.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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..440a4cb5f4 100644 --- a/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py +++ b/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py @@ -4,7 +4,6 @@ class EODHDUpcomingDividendsChainedUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] def initialize(self) -> None: @@ -13,11 +12,11 @@ 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. + # Add a Fundamental Universe selecting the top 100 US Equities by dollar volume. self.add_universe(self._fundamental_filter) - # Second universe: ex-dividend in the next day with a $0.05+ payout, intersected with the fundamental list. + # Add an EODHD Universe for upcoming dividends intersected with the fundamental list. self._universe = self.add_universe(EODHDUpcomingDividends, self._select_assets) - # Rebalance before market open to trade today's intersection. + # Schedule daily rebalancing at 9 AM to trade the universe intersection. self.schedule.on( self.date_rules.every_day("SPY"), self.time_rules.at(9, 0), @@ -30,7 +29,7 @@ def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.Unchan 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. + # Filter for assets with dividends 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] From 0ac2f06bcf9dce2ed2dc2deb13ce29997087e6d3 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Mon, 11 May 2026 07:38:59 -0700 Subject: [PATCH 04/24] Fix formatting in set_holdings call From bd76b7e688bb794dfdbc74087a578bb1413a66b9 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 13:34:53 -0700 Subject: [PATCH 05/24] Update main.py --- .../python/alternative-data-universe-coingeckouniverse/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From b9e8a211caf1c0d0fccc60c5171b4f603b0e3ef4 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 13:55:29 -0700 Subject: [PATCH 06/24] minor changes added seed initial prices; fixed accurate price errors; increased dividend to prevent overtrading --- .../main.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py b/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py index ed2694dd19..38c325cb01 100644 --- a/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py +++ b/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py @@ -8,8 +8,8 @@ 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 + self.settings.seed_initial_prices = True + self.universe_settings.resolution = Resolution.HOUR # Universe of US Equities going ex-dividend in the next day with a meaningful payout. self._universe = self.add_universe(EODHDUpcomingDividends, self._select_assets) @@ -17,16 +17,17 @@ 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[EODHDUpcomingDividends]) -> List[Symbol]: - # Keep names with a dividend over $0.05 paying within one day. + # Keep names with a dividend over $0.75 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] + and d.dividend_date <= self.time + timedelta(1) and d.dividend > 0.75] def _rebalance(self) -> None: if not self._universe.selected: return + securities = [s for s in self._universe.selected if self.securities[s].price] - weight = 1 / len(self._universe.selected) - targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] + weight = 1 / len(securities) + targets = [PortfolioTarget(symbol, weight) for symbol in securities] self.set_holdings(targets, liquidate_existing_holdings=True) From 8b68a69d29326a7c7b78bc8d8a2a5c55097c9482 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 13:58:19 -0700 Subject: [PATCH 07/24] minor changes matching .py performance - initial prices; fixed accurate price errors; increased dividend to prevent overtrading --- .../Main.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs index 078e84a504..10bf83dc88 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs @@ -14,14 +14,15 @@ 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 going ex-dividend in the next day with a meaningful payout. _universe = AddUniverse(data => { - // Keep names with a dividend over $0.05 paying within one day. + // Keep names with a dividend over $0.75 paying within one day. return from d in data.OfType() - where d.DividendDate <= Time.AddDays(1) && d.Dividend > 0.05m + where d.DividendDate <= Time.AddDays(1) && d.Dividend > 0.75m select d.Symbol; }); @@ -36,8 +37,9 @@ 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(); + var weight = 1m / securities.Count; + var targets = securities .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); From 7be5a4b64136adda6174d5a82040b01406b88350 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 14:43:16 -0700 Subject: [PATCH 08/24] minor changes matching .cs performance seed initial price; resolution intraday; time_rule; price guard; --- .../main.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/project-templates/python/alternative-data-universe-eodhdupcomingearnings/main.py b/project-templates/python/alternative-data-universe-eodhdupcomingearnings/main.py index 7e7597da05..be8e4b857e 100644 --- a/project-templates/python/alternative-data-universe-eodhdupcomingearnings/main.py +++ b/project-templates/python/alternative-data-universe-eodhdupcomingearnings/main.py @@ -8,25 +8,27 @@ 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.settings.seed_initial_prices = True + self.universe_settings.resolution = Resolution.HOUR + # Universe of US Equities reporting earnings in the next 3 days with estimate > 1.5. 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) + self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.at(9, 31, 0), self._rebalance) def _select_assets(self, data: List[EODHDUpcomingEarnings]) -> List[Symbol]: - # Keep names with a positive analyst estimate ahead of the report. + # Keep names with an analyst estimate over 1.5 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] + if d.report_date and d.estimate and + d.report_date <= self.time + timedelta(3) and d.estimate > 1.5] 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 = 1 / len(securities) + targets = [PortfolioTarget(symbol, weight) for symbol in securities] self.set_holdings(targets, liquidate_existing_holdings=True) From b7cc971fef2355db5f053607ac2953d3cf8d8138 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 14:46:58 -0700 Subject: [PATCH 09/24] minor changes matching .py performance seed initial price; resolution intraday; time_rule; price guard; increase estimate to prevent overtrading --- .../Main.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs index 2737b1b01b..dbaea81641 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs @@ -14,19 +14,20 @@ 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 reporting earnings in the next 3 days with a positive estimate. + // Universe of US Equities reporting earnings in the next 3 days with estimate > 1.5. _universe = AddUniverse(data => { - // Keep names with a positive analyst estimate ahead of the report. + // Keep names with an analyst estimate over 1.5 ahead of the report. return from d in data.OfType() - where d.ReportDate <= Time.AddDays(3) && d.Estimate > 0m + where d.ReportDate <= Time.AddDays(3) && d.Estimate > 1.5m select d.Symbol; }); // Rebalance shortly after the open so today's universe is locked in. - Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance); + Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 31, 0), Rebalance); } private void Rebalance() @@ -36,8 +37,14 @@ 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 = 1m / securities.Count; + var targets = securities .Select(symbol => new PortfolioTarget(symbol, weight)) .ToList(); From 76a87327a72af244284816b9a1f915520612195e Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:04:08 -0700 Subject: [PATCH 10/24] Add check for empty securities list before processing --- .../alternative-data-universe-eodhdupcomingdividends/Main.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs index 10bf83dc88..2f420efc04 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs @@ -38,6 +38,10 @@ private void Rebalance() } var securities = _universe.Selected.Where(s => Securities[s].Price > 0).ToList(); + if (securities.Count == 0) + { + return; + } var weight = 1m / securities.Count; var targets = securities .Select(symbol => new PortfolioTarget(symbol, weight)) From 9ec74fc0b4599a960a0429231c6ac807de0e4f93 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:05:49 -0700 Subject: [PATCH 11/24] Prevent rebalancing with empty securities list Add check for empty securities list before rebalancing. --- .../alternative-data-universe-eodhdupcomingdividends/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py b/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py index 38c325cb01..af9dcec609 100644 --- a/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py +++ b/project-templates/python/alternative-data-universe-eodhdupcomingdividends/main.py @@ -26,7 +26,8 @@ def _rebalance(self) -> None: if not self._universe.selected: return securities = [s for s in self._universe.selected if self.securities[s].price] - + if not securities: + return weight = 1 / len(securities) targets = [PortfolioTarget(symbol, weight) for symbol in securities] From 3292a3bf090f6b6ba200821167c07edb1ac85e4e Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:06:53 -0700 Subject: [PATCH 12/24] fix --- .../alternative-data-universe-eodhdupcomingearnings/Main.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs index dbaea81641..21f14c53f9 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs @@ -42,7 +42,6 @@ private void Rebalance() { return; } - var weight = 1m / securities.Count; var targets = securities .Select(symbol => new PortfolioTarget(symbol, weight)) From f1b7016eb203415ee69684c5662eb83ce021e72d Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:09:08 -0700 Subject: [PATCH 13/24] revert --- .../main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 6781107017..37341e7c3e 100644 --- a/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py @@ -12,11 +12,11 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.MINUTE - # Select the top 100 US equities by dollar volume for fundamental filtering. + # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Filter for stocks with positive Brain ML rankings across all horizons. + # Second universe: positive Brain ML rankings across 2-, 3-, and 5-day horizons, intersected with the fundamental list. self._universe = self.add_universe(BrainStockRankingUniverse, self._select_assets) - # Rebalance daily before market open to trade the selected universe. + # Rebalance before market open to trade today's intersection. self.schedule.on( self.date_rules.every_day("SPY"), self.time_rules.at(9, 0), @@ -29,7 +29,7 @@ def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.Unchan return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[BrainStockRankingUniverse]) -> List[Symbol]: - # Keep stocks with positive rankings across all three horizons. + # 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 From 7c446b1c6212143b437933ed38b4cbc2da96d249 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:09:44 -0700 Subject: [PATCH 14/24] revert --- .../main.py | 1 + 1 file changed, 1 insertion(+) 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 37341e7c3e..afc7c1982a 100644 --- a/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py +++ b/project-templates/python/alternative-data-chain-universe-brainstockrankinguniverse/main.py @@ -4,6 +4,7 @@ class BrainStockRankingChainedUniverseAlgorithm(QCAlgorithm): + _fundamental: list[Symbol] = [] def initialize(self) -> None: From 8c744a7265376046f622fb56d33219857bc1798d Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:13:44 -0700 Subject: [PATCH 15/24] revert --- .../main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 440a4cb5f4..92718ed433 100644 --- a/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py +++ b/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py @@ -12,11 +12,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 selecting the top 100 US Equities by dollar volume. + # First universe: top 100 US Equities by dollar volume; emits Universe.UNCHANGED. self.add_universe(self._fundamental_filter) - # Add an EODHD Universe for upcoming dividends intersected with the fundamental list. + # Second universe: ex-dividend in the next day with a $0.05+ payout, intersected with the fundamental list. self._universe = self.add_universe(EODHDUpcomingDividends, self._select_assets) - # Schedule daily rebalancing at 9 AM to trade the universe intersection. + # Rebalance before market open to trade today's intersection. self.schedule.on( self.date_rules.every_day("SPY"), self.time_rules.at(9, 0), @@ -29,7 +29,7 @@ def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.Unchan return Universe.UNCHANGED def _select_assets(self, alt_coarse: List[EODHDUpcomingDividends]) -> List[Symbol]: - # Filter for assets with dividends over $0.05 paying within one day. + # 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] From e243f75a80ccab83be508753433140ca0b2abad2 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:17:21 -0700 Subject: [PATCH 16/24] Change universe resolution from Daily to Hour --- .../alternative-data-universe-eodhdupcomingdividends/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs index 2f420efc04..c49100421a 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingdividends/Main.cs @@ -16,7 +16,7 @@ public override void Initialize() SetCash(100000); Settings.SeedInitialPrices = true; - UniverseSettings.Resolution = Resolution.Daily; + UniverseSettings.Resolution = Resolution.Hour; // Universe of US Equities going ex-dividend in the next day with a meaningful payout. _universe = AddUniverse(data => { From 27a95394eefab9c36a4d5c9235b789af7c81595d Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:17:53 -0700 Subject: [PATCH 17/24] Change universe resolution from Daily to Hour --- .../alternative-data-universe-eodhdupcomingearnings/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs b/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs index 21f14c53f9..31d972b3ec 100644 --- a/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs +++ b/project-templates/csharp/alternative-data-universe-eodhdupcomingearnings/Main.cs @@ -16,7 +16,7 @@ public override void Initialize() SetCash(100000); Settings.SeedInitialPrices = true; - UniverseSettings.Resolution = Resolution.Daily; + UniverseSettings.Resolution = Resolution.Hour; // Universe of US Equities reporting earnings in the next 3 days with estimate > 1.5. _universe = AddUniverse(data => { From cdde230b234b10bf955911d1ee21bf72f5f7df39 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:18:25 -0700 Subject: [PATCH 18/24] revert --- .../main.py | 1 + 1 file changed, 1 insertion(+) 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 92718ed433..319832377a 100644 --- a/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py +++ b/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py @@ -4,6 +4,7 @@ class EODHDUpcomingDividendsChainedUniverseAlgorithm(QCAlgorithm): + _fundamental: list[Symbol] = [] def initialize(self) -> None: From bcfa1a0ce72e2208e3834a6a484ce3c776247fda Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:21:32 -0700 Subject: [PATCH 19/24] fix --- .../main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 319832377a..218fb5a1a8 100644 --- a/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py +++ b/project-templates/python/alternative-data-chain-universe-eodhdupcomingdividends/main.py @@ -4,7 +4,7 @@ class EODHDUpcomingDividendsChainedUniverseAlgorithm(QCAlgorithm): - + _fundamental: list[Symbol] = [] def initialize(self) -> None: From 5b24c75bfac7abad9e49e50398cf15fb176cb053 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:40:18 -0700 Subject: [PATCH 20/24] remove the 2nd universe, make filtering more strict --- .../Main.cs | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/project-templates/csharp/alternative-data-universe-brainsentimentindicatoruniverse/Main.cs b/project-templates/csharp/alternative-data-universe-brainsentimentindicatoruniverse/Main.cs index fbe6afce81..6a1ae2e8bf 100644 --- a/project-templates/csharp/alternative-data-universe-brainsentimentindicatoruniverse/Main.cs +++ b/project-templates/csharp/alternative-data-universe-brainsentimentindicatoruniverse/Main.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using QuantConnect.Algorithm.Framework.Portfolio; -using QuantConnect.Data.Fundamental; using QuantConnect.Data.UniverseSelection; using QuantConnect.DataSource; @@ -10,7 +9,6 @@ namespace QuantConnect.Algorithm.CSharp { public class BrainSentimentIndicatorChainedUniverseAlgorithm : QCAlgorithm { - private List _fundamental = []; private Universe _universe; public override void Initialize() @@ -20,24 +18,13 @@ public override void Initialize() SetCash(100000); Settings.SeedInitialPrices = true; UniverseSettings.Resolution = Resolution.Daily; - // Add a fundamental universe to track the most liquid US Equities by dollar volume. - AddUniverse(fundamental => - { - _fundamental = fundamental - .OrderBy(c => c.DollarVolume) - .TakeLast(100) - .Select(c => c.Symbol) - .ToList(); - return Universe.Unchanged; - }); - // Add a Brain Sentiment universe, restricted to high-sentiment names within the fundamental list. + // Add a Brain Sentiment universe, restricted to high-sentiment names with strong coverage. _universe = AddUniverse( - // Keep names with both active mention coverage and positive 7-day sentiment. + // Keep names with both active mention coverage 15+ articles and positive 7-day sentiment above 0.5. altCoarse => altCoarse .OfType() - .Where(d => d.TotalArticleMentions7Days > 0m && d.Sentiment7Days > 0m) + .Where(d => d.TotalArticleMentions7Days > 15m && d.Sentiment7Days > 0.5m) .Select(d => d.Symbol) - .Intersect(_fundamental) ); // Rebalance every day at 9am. Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance); From 4fc38a19413044cdfdef7821b973a5c93f1dd601 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 15:41:02 -0700 Subject: [PATCH 21/24] Refine asset selection criteria in BrainSentimentIndicator --- .../main.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/project-templates/python/alternative-data-universe-brainsentimentindicatoruniverse/main.py b/project-templates/python/alternative-data-universe-brainsentimentindicatoruniverse/main.py index f983a888f8..d1da2ecbe2 100644 --- a/project-templates/python/alternative-data-universe-brainsentimentindicatoruniverse/main.py +++ b/project-templates/python/alternative-data-universe-brainsentimentindicatoruniverse/main.py @@ -4,7 +4,6 @@ class BrainSentimentIndicatorUniverseAlgorithm(QCAlgorithm): - _fundamental: list[Symbol] = [] def initialize(self) -> None: self.set_start_date(2024, 9, 1) @@ -12,9 +11,7 @@ def initialize(self) -> None: self.set_cash(100_000) self.settings.seed_initial_prices = True self.universe_settings.resolution = Resolution.DAILY - # Add a fundamental universe to track the most liquid US Equities by dollar volume. - self.add_universe(self._fundamental_filter) - # Add a Brain Sentiment universe, restricted to high-sentiment names within the fundamental list. + # Add a Brain Sentiment universe, restricted to high-sentiment names with strong coverage. self._universe = self.add_universe(BrainSentimentIndicatorUniverse, self._select_assets) # Rebalance every day at 9am. self.schedule.on( @@ -23,16 +20,11 @@ def initialize(self) -> None: self._rebalance ) - def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - self._fundamental = [f.symbol for f in sorted(fundamental, key=lambda f: f.dollar_volume)[-100:]] - return Universe.UNCHANGED - def _select_assets(self, data: List[BrainSentimentIndicatorUniverse]) -> List[Symbol]: - # Keep names with both active mention coverage and positive 7-day sentiment. - alt = [d.symbol for d in data - 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 mention coverage 15+ articles and positive 7-day sentiment above 0.5. + return [d.symbol for d in data + if d.total_article_mentions_7_days and d.total_article_mentions_7_days > 15 and + d.sentiment_7_days and d.sentiment_7_days > 0.5] def _rebalance(self) -> None: if not self._universe.selected: From 6031b8da20ac3941873713aa982d9b2692f7cd19 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 16:13:22 -0700 Subject: [PATCH 22/24] Refactor asset selection and remove fundamental universe Removed the fundamental universe tracking and adjusted asset selection criteria to filter stocks with positive rankings above 0.05. --- .../main.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/project-templates/python/alternative-data-universe-brainstockrankinguniverse/main.py b/project-templates/python/alternative-data-universe-brainstockrankinguniverse/main.py index 4ce77efc13..ab7bf0ddcf 100644 --- a/project-templates/python/alternative-data-universe-brainstockrankinguniverse/main.py +++ b/project-templates/python/alternative-data-universe-brainstockrankinguniverse/main.py @@ -4,15 +4,12 @@ class BrainStockRankingUniverseAlgorithm(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(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 Brain Stock Ranking universe, restricted to high-ranking names within the fundamental list. self._universe = self.add_universe(BrainStockRankingUniverse, self._select_assets) # Schedule daily rebalancing at 9:00 AM before market open. @@ -22,18 +19,12 @@ def initialize(self) -> None: self._rebalance ) - def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse: - # Store the top 50 symbols by dollar volume for use in the alt-data 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[BrainStockRankingUniverse]) -> List[Symbol]: # Filter for stocks with positive rankings across 2-day, 3-day, and 5-day horizons. - alt = [d.symbol for d in data - 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] + return [d.symbol for d in data + if d.rank_2_days and d.rank_2_days > 0.05 and + d.rank_3_days and d.rank_3_days > 0.05 and + d.rank_5_days and d.rank_5_days > 0.05] def _rebalance(self) -> None: if not self._universe.selected: From e790dbd42561f8a8c79917a9de770fd909c0ad4c Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 16:17:54 -0700 Subject: [PATCH 23/24] Clarify comment on Brain Stock Ranking universe Updated comment to clarify the criteria for the Brain Stock Ranking universe. --- .../alternative-data-universe-brainstockrankinguniverse/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project-templates/python/alternative-data-universe-brainstockrankinguniverse/main.py b/project-templates/python/alternative-data-universe-brainstockrankinguniverse/main.py index ab7bf0ddcf..7d947320f9 100644 --- a/project-templates/python/alternative-data-universe-brainstockrankinguniverse/main.py +++ b/project-templates/python/alternative-data-universe-brainstockrankinguniverse/main.py @@ -10,7 +10,7 @@ def initialize(self) -> None: self.set_end_date(2024, 12, 31) self.set_cash(100_000) self.settings.seed_initial_prices = True - # Add a Brain Stock Ranking universe, restricted to high-ranking names within the fundamental list. + # Add a Brain Stock Ranking universe, restricted to high-ranking names with strong multi-horizon signals. self._universe = self.add_universe(BrainStockRankingUniverse, self._select_assets) # Schedule daily rebalancing at 9:00 AM before market open. self.schedule.on( From 5701cebfa66591f53c8fc701369f9fae57a3bdf4 Mon Sep 17 00:00:00 2001 From: Rudy Osuna Date: Tue, 12 May 2026 16:18:28 -0700 Subject: [PATCH 24/24] Refine universe filtering criteria for rankings --- .../Main.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/project-templates/csharp/alternative-data-universe-brainstockrankinguniverse/Main.cs b/project-templates/csharp/alternative-data-universe-brainstockrankinguniverse/Main.cs index 87e3c90ec2..a2dcad0556 100644 --- a/project-templates/csharp/alternative-data-universe-brainstockrankinguniverse/Main.cs +++ b/project-templates/csharp/alternative-data-universe-brainstockrankinguniverse/Main.cs @@ -9,7 +9,6 @@ namespace QuantConnect.Algorithm.CSharp public class BrainStockRankingUniverseAlgorithm : QCAlgorithm { private Universe _universe; - private List _fundamental = []; public override void Initialize() { @@ -17,24 +16,13 @@ public override void Initialize() SetEndDate(2024, 12, 31); SetCash(100000); Settings.SeedInitialPrices = true; - // Add a fundamental universe to track the most liquid US equities by dollar volume. - AddUniverse(fundamental => - { - // Store the top 50 symbols by dollar volume for use in the alt-data universe. - _fundamental = fundamental - .OrderBy(f => f.DollarVolume) - .TakeLast(50) - .Select(f => f.Symbol) - .ToList(); - return Universe.Unchanged; - }); - // Add a Brain Stock Ranking universe, restricted to high-ranking names within the fundamental list. + // Add a Brain Stock Ranking universe, restricted to high-ranking names with strong multi-horizon signals. _universe = AddUniverse( + // Filter for stocks with positive rankings across 2-day, 3-day, and 5-day horizons. data => data .OfType() - .Where(d => d.Rank2Days > 0m && d.Rank3Days > 0m && d.Rank5Days > 0m) + .Where(d => d.Rank2Days > 0.05m && d.Rank3Days > 0.05m && d.Rank5Days > 0.05m) .Select(d => d.Symbol) - .Where(symbol => _fundamental.Contains(symbol)) ); // Schedule daily rebalancing at 9:00 AM before market open. Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0), Rebalance);