Building a Date-Specific Equity Universe from an External Ticker File
Summary
This example shows how to construct a changing equity universe from a downloaded file that maps dates to ticker lists. The algorithm parses each row into a date and US equity symbols, then uses the symbols for that date as its coarse universe selection. Fine selection adds a market-capitalization filter, retaining companies above the stated threshold of ten billion dollars. This illustrates how external membership data can drive historical universe selection.
In backtests, the file is loaded once and reused; in live mode, the example refreshes it every twelve hours and returns the current date’s row between updates. If a date is absent, it leaves the universe unchanged. The sample logs market capitalization and reports securities added to the universe. It covers data ingestion and universe construction rather than an entry or exit strategy, and its brief date range and external-file dependency provide no evidence of investment performance. Data format, availability, and historical accuracy are practical dependencies.
Key ideas
- A dated ticker file can define which equities enter a universe on each date.
- Coarse selection uses the date-specific ticker list, while fine selection applies a market-capitalization threshold.
- The sample loads data once for a backtest and refreshes it on a schedule in live trading.
- Missing dates leave the existing universe unchanged.
- The example describes universe plumbing and provides no trading-performance evidence.
Tags
Full text
# DropboxCoarseFineAlgorithm
# DropboxCoarseFineAlgorithm
In this algorithm, we fetch a list of tickers with corresponding dates from a file on Dropbox. We then create a fine fundamental universe which contains those symbols on their respective dates.###
## Source (Apache-2.0)
```python
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
### <summary>
### In this algorithm, we fetch a list of tickers with corresponding dates from a file on Dropbox.
### We then create a fine fundamental universe which contains those symbols on their respective dates.###
### </summary>
### <meta name="tag" content="download" />
### <meta name="tag" content="universes" />
### <meta name="tag" content="custom data" />
class DropboxCoarseFineAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2019, 9, 23) # Set Start Date
self.set_end_date(2019, 9, 30) # Set End Date
self.set_cash(100000) # Set Strategy Cash
self.add_universe(self.select_coarse, self.select_fine)
self.universe_data = None
self.next_update = datetime(1, 1, 1) # Minimum datetime
self.url = "https://www.dropbox.com/s/x2sb9gaiicc6hm3/tickers_with_dates.csv?dl=1"
def on_end_of_day(self, symbol: Symbol) -> None:
self.debug(f"{self.time.date()} {symbol.value} with Market Cap: ${self.securities[symbol].fundamentals.market_cap}")
def select_coarse(self, coarse):
return self.get_symbols()
def select_fine(self, fine):
symbols = self.get_symbols()
# Return symbols from our list which have a market capitalization of at least 10B
return [f.symbol for f in fine if f.market_cap > 1e10 and f.symbol in symbols]
def get_symbols(self):
# In live trading update every 12 hours
if self.live_mode:
if self.time < self.next_update:
# Return today's row
return self.universe_data[self.time.date()]
# When updating set the new reset time.
self.next_update = self.time + timedelta(hours=12)
self.universe_data = self.parse(self.url)
# In backtest load once if not set, then just use the dates.
if not self.universe_data:
self.universe_data = self.parse(self.url)
# Check if contains the row we need
if self.time.date() not in self.universe_data:
return Universe.UNCHANGED
return self.universe_data[self.time.date()]
def parse(self, url):
# Download file from url as string
file = self.download(url).split("\n")
# # Remove formatting characters
data = [x.replace("\r", "").replace(" ", "") for x in file]
# # Split data by date and symbol
split_data = [x.split(",") for x in data]
# Dictionary to hold list of active symbols for each date, keyed by date
symbols_by_date = {}
# Parse data into dictionary
for arr in split_data:
date = datetime.strptime(arr[0], "%Y%m%d").date()
symbols = [Symbol.create(ticker, SecurityType.EQUITY, Market.USA) for ticker in arr[1:]]
symbols_by_date[date] = symbols
return symbols_by_date
def on_securities_changed(self, changes):
self.log(f"Added Securities: {[security.symbol.value for security in changes.added_securities]}")
```Shown in full with attribution under the source's licence. Licence: Apache-2.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.