Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Data.UniverseSelection;
Expand All @@ -7,26 +8,37 @@ namespace QuantConnect.Algorithm.CSharp
{
public class EODHDUpcomingDividendsUniverseAlgorithm : QCAlgorithm
{
private List<Symbol> _fundamental = [];
private Universe _universe;

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<EODHDUpcomingDividends>(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<EODHDUpcomingDividends>()
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<EODHDUpcomingDividends>(
// Filter for symbols with dividends over $0.05 paying within one day.
data => data
.OfType<EODHDUpcomingDividends>()
.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()
Expand All @@ -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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid bike shedding changes like this @Ruuudy1

Image

}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Data.UniverseSelection;
Expand All @@ -7,26 +8,37 @@ namespace QuantConnect.Algorithm.CSharp
{
public class EODHDUpcomingEarningsUniverseAlgorithm : QCAlgorithm
{
private List<Symbol> _fundamental = [];
private Universe _universe;

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<EODHDUpcomingEarnings>(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<EODHDUpcomingEarnings>()
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<EODHDUpcomingEarnings>(
// Filter for symbols with a positive analyst estimate reporting within the next 3 days.
data => data
.OfType<EODHDUpcomingEarnings>()
.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()
Expand All @@ -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);
}
}
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 @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Loading