quantlab.dataset.crsp.tickers.CrspTickerLookup#

class quantlab.dataset.crsp.tickers.CrspTickerLookup(sidecar_path)[source]#

Bases: TickerLookup

Look up the ticker a PERMNO had on a given day, from one {zarr}.crsp_tickers.json.

The file is read lazily, on first use. The callers are display code deep inside a backtest or a log call that only has a store path; if the constructor read the disk, creating a lookup “just in case” would cost a file read at every such place.

Parameters:
sidecar_pathstr or Path

Path of the ticker sidecar. A CRSP or NBBO dataset’s ticker_lookup() builds one over its own sidecar, and beside_store derives it from a store path.

Attributes:
sidecar_pathPath

The sidecar path.

Examples

>>> from datetime import date
>>> from quantlab.dataset.crsp.tickers import CrspTickerLookup
>>> lookup = CrspTickerLookup.beside_store("data/data/us_equity/1d/crsp.zarr")
>>> lookup.as_of(13407, date(2022, 6, 8)), lookup.as_of(13407, date(2022, 6, 9))
('FB', 'META')
>>> lookup.label([13407, 14593, 99999], date(2020, 1, 1))
['FB', 'AAPL', '99999']
>>> lookup.names([13407], date(2022, 6, 9))
[SymbolName(ticker='META', company=None)]
as_of(permno, day)[source]#

Return the ticker permno had on day, or None.

Both ends of an interval are inclusive. PERMNO 13407’s FB interval ends on 2022-06-08 and its META interval starts on 2022-06-09, so each day belongs to exactly one of them. None means either “no interval covers this day” or “this sidecar does not know this PERMNO”; no caller needs to tell the two apart.

The intervals are searched one by one: a security’s whole naming history is only a handful of intervals, so an index would cost more than it saves.

Parameters:
permnoint

The PERMNO, as an int or anything int() accepts.

daydate

The date to look up; anything whose str() starts with an ISO date works.

Raises:
FileNotFoundError

If the sidecar is absent.

ValueError

If it cannot be parsed or has the wrong shape (top level, intervals, or an interval without start, end or ticker).

Examples

>>> lookup.as_of(13407, date(2022, 6, 8))
'FB'
>>> lookup.as_of(13407, date(2000, 1, 1)) is None
True
classmethod beside_store(zarr_file_path)[source]#

Return the lookup for the sidecar written next to zarr_file_path.

For a caller holding only a store path. A caller holding the dataset asks it instead (CrspStockDataset.ticker_lookup()), which is how a backtest and a membership report get it.

The import is inside the function on purpose. quantlab.dataset.crsp defines the constant and also loads polars and the whole converter, which a log line does not need. It is also this module’s own parent package, and importing it is only safe at call time, when the parent is fully initialized.

Parameters:
zarr_file_pathstr or Path

Path of the CRSP Zarr store.

Returns:
CrspTickerLookup

A lookup over <zarr_file_path>.crsp_tickers.json.

Examples

>>> CrspTickerLookup.beside_store("data/data/us_equity/1d/crsp.zarr")
CrspTickerLookup('data/data/us_equity/1d/crsp.zarr.crsp_tickers.json')
names(permnos, day)[source]#

Return the ticker of each of permnos on day, in order.

This is the entry point for display code (see TickerLookup). It takes a batch because every caller prints a list (a missing-member report, a dropped-symbol warning, the settlement records of one date). company is always None: the sidecar records no company name.

It never raises. An unknown PERMNO falls back to its own digits, and so does every PERMNO when the sidecar is missing, unparseable or wrongly shaped. Every fallback caused by the file goes through _degrade, which warns once per object. A value that is not an integer at all (a string symbol from another vendor) is passed through unchanged and does not count as a fallback.

Errors are caught in two places because a damaged file can fail at two points: {"intervals": [1, 2, 3]} fails while the table is read, while {"intervals": {"13407": [{"ticker": "FB"}]}} reads as a good table and only fails inside as_of. Both places catch _UNUSABLE and nothing wider. except Exception is avoided on purpose, so a real bug in this module still reaches the caller instead of being printed as digits that look like a genuine “no name on that day” answer.

Parameters:
permnosSequence

PERMNOs as ints or digit strings, or plain string symbols.

daydate

The date whose tickers to use.

Returns:
list of SymbolName

One name per input, in the input’s order.

Examples

>>> lookup.names([13407, 99999], date(2022, 6, 8))
[SymbolName(ticker='FB', company=None), SymbolName(ticker='99999', company=None)]
>>> lookup.names(["AAPL"], date(2020, 1, 1))
[SymbolName(ticker='AAPL', company=None)]
property payload#

Return the sidecar as parsed JSON, read at most once per instance.

Raises:
FileNotFoundError

If the sidecar is missing. The message names the class, the path and the fix (a rebuild), because the file is written by a conversion and cannot be created by hand.

ValueError

If the file cannot be read or parsed. This includes a RecursionError from JSON nested deeper than the parser allows, which would otherwise escape as a RuntimeError without the explanatory message.

Examples

>>> sorted(lookup.payload)
['generated_from', 'intervals', 'vintage_product_end']
property product_end#

Return the last date of the CRSP data the sidecar was built from, or None.

CRSP publishes data in versions, each ending on a product end date. None means the sidecar records no version, which is a valid state, not damage. Only a payload whose top level is not a JSON object is refused, with the same error as_of gives; a broken interval table does not affect this property.

Raises:
FileNotFoundError

If the sidecar is absent.

ValueError

If it cannot be parsed or its top level is not an object.

Examples

>>> lookup.product_end
datetime.date(2025, 12, 31)