Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
13584ad
Refactor comments and improve code formatting
Ruuudy1 May 11, 2026
fff3e24
Update main.py
Ruuudy1 May 11, 2026
27ae8c4
Update main.py
Ruuudy1 May 11, 2026
0ac2f06
Fix formatting in set_holdings call
Ruuudy1 May 11, 2026
1ff811f
Merge branch 'QuantConnect:master' into master
Ruuudy1 May 11, 2026
3a2a8ac
Merge branch 'QuantConnect:master' into master
Ruuudy1 May 11, 2026
b328ba6
Merge branch 'QuantConnect:master' into master
Ruuudy1 May 12, 2026
726b2e0
Merge branch 'QuantConnect:master' into master
Ruuudy1 May 12, 2026
bd76b7e
Update main.py
Ruuudy1 May 12, 2026
b9e8a21
minor changes
Ruuudy1 May 12, 2026
8b68a69
minor changes matching .py performance
Ruuudy1 May 12, 2026
7be5a4b
minor changes matching .cs performance
Ruuudy1 May 12, 2026
b7cc971
minor changes matching .py performance
Ruuudy1 May 12, 2026
76a8732
Add check for empty securities list before processing
Ruuudy1 May 12, 2026
9ec74fc
Prevent rebalancing with empty securities list
Ruuudy1 May 12, 2026
3292a3b
fix
Ruuudy1 May 12, 2026
f1b7016
revert
Ruuudy1 May 12, 2026
7c446b1
revert
Ruuudy1 May 12, 2026
8c744a7
revert
Ruuudy1 May 12, 2026
e243f75
Change universe resolution from Daily to Hour
Ruuudy1 May 12, 2026
27a9539
Change universe resolution from Daily to Hour
Ruuudy1 May 12, 2026
cdde230
revert
Ruuudy1 May 12, 2026
bcfa1a0
fix
Ruuudy1 May 12, 2026
5b24c75
remove the 2nd universe, make filtering more strict
Ruuudy1 May 12, 2026
4fc38a1
Refine asset selection criteria in BrainSentimentIndicator
Ruuudy1 May 12, 2026
6031b8d
Refactor asset selection and remove fundamental universe
Ruuudy1 May 12, 2026
e790dbd
Clarify comment on Brain Stock Ranking universe
Ruuudy1 May 12, 2026
5701ceb
Refine universe filtering criteria for rankings
Ruuudy1 May 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.DataSource;

namespace QuantConnect.Algorithm.CSharp
{
public class BrainSentimentIndicatorChainedUniverseAlgorithm : QCAlgorithm
{
private List<Symbol> _fundamental = [];
private Universe _universe;

public override void Initialize()
Expand All @@ -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<BrainSentimentIndicatorUniverse>(
// 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<BrainSentimentIndicatorUniverse>()
.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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,20 @@ namespace QuantConnect.Algorithm.CSharp
public class BrainStockRankingUniverseAlgorithm : QCAlgorithm
{
private Universe _universe;
private List<Symbol> _fundamental = [];

public override void Initialize()
{
SetStartDate(2024, 9, 1);
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<BrainStockRankingUniverse>(
// Filter for stocks with positive rankings across 2-day, 3-day, and 5-day horizons.
data => data
.OfType<BrainStockRankingUniverse>()
.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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
UniverseSettings.Resolution = Resolution.Hour;
// Universe of US Equities going ex-dividend in the next day with a meaningful payout.
_universe = AddUniverse<EODHDUpcomingDividends>(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<EODHDUpcomingDividends>()
where d.DividendDate <= Time.AddDays(1) && d.Dividend > 0.05m
where d.DividendDate <= Time.AddDays(1) && d.Dividend > 0.75m
select d.Symbol;
});

Expand All @@ -36,8 +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 = 1m / securities.Count;
var targets = securities
.Select(symbol => new PortfolioTarget(symbol, weight))
.ToList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
UniverseSettings.Resolution = Resolution.Hour;
// Universe of US Equities reporting earnings in the next 3 days with estimate > 1.5.
_universe = AddUniverse<EODHDUpcomingEarnings>(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<EODHDUpcomingEarnings>()
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()
Expand All @@ -36,8 +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 = 1m / securities.Count;
var targets = securities
.Select(symbol => new PortfolioTarget(symbol, weight))
.ToList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@


class BrainSentimentIndicatorUniverseAlgorithm(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
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(
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@


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.
# 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(
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
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)

# 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)

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

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)
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Loading