beacon.data¶
Market and reference data access: MarketData/ReferenceData wrap tabular
sources, and DataFetcher provides the unified query interface used
throughout the calculation and backtest layers.
data ¶
The init.py for the 'data' module.
This module handles fetching, parsing, and providing financial data.
MarketData ¶
Time-series data container backed by a MultiIndex DataFrame.
The source file must contain at least IDENTIFIER and DATE columns.
After loading the DataFrame is indexed on (IDENTIFIER, DATE) and sorted,
enabling fast .loc slicing by identifier or list of identifiers.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/base.py
identifiers
property
¶
Unique identifiers present in the dataset.
Cached, because this is not the cheap property it reads as. It scans
the whole MultiIndex, and fetch_fx_rates consults it on every call
to decide whether a pair exists -- which the calculator makes once per
foreign holding per day. Against a single-currency universe that never
fired; against a global one it turned an O(rows) scan into an inner
loop, and an index over eighty names took longer than the entire rest
of the test suite.
Keyed on the frame's identity rather than a flag, so replacing _df
invalidates it automatically instead of relying on every future
mutation remembering to.
columns
property
¶
Non-index column names.
Cached on the frame's identity, like identifiers and for the same
reason (BN-214). _market_scalar asks column not in market.columns
on every price read -- 186,400 times over a 200-name three-year
run -- and this built a fresh list of strings each time to answer a
membership test.
sessions
property
¶
The distinct dates the dataset carries, ascending.
Cached on the frame's identity, for the reason the identifiers above
are (BN-190). Materialising the DATE level is an O(rows) take over the
whole frame, and date_range and last_session_on_or_before each did
it on every call — which resolve_session makes once per name while
a methodology walks a universe. On a 1,600-name preview that was 59% of
the runtime spent re-deriving a constant: every name on a given date
resolves to the same session, and the frame does not move underneath
them.
date_range
property
¶
(earliest, latest) timestamps in the dataset.
from_dataframe
classmethod
¶
Create a MarketData instance from an existing DataFrame.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/base.py
last_session_on_or_before ¶
The latest date the dataset carries at or before date.
A date inside the coverage that has no rows is an ordinary closed market, and this is the session that was in force through it. None when the dataset begins after date, since then there is no earlier session to be in force.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/base.py
session_columns ¶
Every column's values for one session, keyed by identifier (BN-213).
The read the daily loops make eight hundred times a run, done without
touching pandas. get() filters the whole frame to find one day's rows
-- the frame is indexed (IDENTIFIER, DATE), so a single day's rows are
scattered through it rather than adjacent, and finding them costs what
the frame costs. Measured over 156,400 rows: 13.5 ms a day, which is
most of an index calculation.
This uses :meth:_date_index instead: the row positions for a date are
already known, so the read is a gather and two dict builds.
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
dict[str, dict[str, object]]
|
|
set[str]
|
what a :class: |
|
tuple[dict[str, dict[str, object]], set[str]]
|
DataFrame is built on the way. Empty for a date the data has no |
|
tuple[dict[str, dict[str, object]], set[str]]
|
rows on. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/base.py
get ¶
get(
identifier: str | list[str],
start_date: str | None = None,
end_date: str | None = None,
columns: list[str] | None = None,
) -> pd.DataFrame
Return data for one or more identifiers, optionally filtered.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str | list[str]
|
Single identifier or list of identifiers. |
required |
start_date
|
str | None
|
Date string to slice the start of the date range. |
None
|
end_date
|
str | None
|
Date string to slice the end of the date range. |
None
|
columns
|
list[str] | None
|
Subset of columns to return. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
DataFrame
|
pd.DataFrame: Single identifier: indexed by |
|
identifiers |
DataFrame
|
MultiIndexed by |
DataFrame
|
DataFrame if no matching data is found. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/base.py
ReferenceData ¶
Reference data container with validity ranges.
The source file must contain IDENTIFIER, DATE_FROM, and DATE_TO
columns. DATE_TO may be NaT to indicate a currently-active record.
Indexed on IDENTIFIER (non-unique, since an identifier may have
multiple validity periods).
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/base.py
identifiers
property
¶
Unique identifiers present in the dataset. Cached, as above.
from_dataframe
classmethod
¶
Create a ReferenceData instance from an existing DataFrame.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/base.py
get ¶
get(
identifier: str | list[str],
date: str | None = None,
columns: list[str] | None = None,
) -> pd.DataFrame
Return reference data for one or more identifiers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str | list[str]
|
Single identifier or list of identifiers. |
required |
date
|
str | None
|
Point-in-time date. If provided, only rows where
|
None
|
columns
|
list[str] | None
|
Subset of columns to return. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Indexed by |
DataFrame
|
match. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/base.py
DataFetcher ¶
DataFetcher(
market_data: MarketData,
reference_data: ReferenceData | None = None,
corporate_actions: CorporateActions | None = None,
features: FeatureData | None = None,
fx_policy: str = DEFAULT_FX_POLICY,
max_price_staleness_days: int | None = None,
free_float_backfill_days: int = DEFAULT_FREE_FLOAT_BACKFILL_DAYS,
)
Unified query interface over MarketData and ReferenceData.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
market_data
|
MarketData
|
Time-series market data container. |
required |
reference_data
|
ReferenceData | None
|
Reference data container. |
None
|
corporate_actions
|
CorporateActions | None
|
Action history. Absent means an empty history rather than None, so callers never have to check before asking — "this instrument paid nothing" and "we hold no action data" give the same answer to every question this class can be asked. |
None
|
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | |
fx_pairs
property
¶
Currency pairs held in the market data.
A pair is stored as an ordinary market identifier named
f"{from}{to}" (BN-128), so nothing in the frame separates it from
an instrument except what it carries: RATE is populated on a pair
and null on everything else. That is the discriminator, rather than a
name pattern -- an instrument legitimately called EURUSD would be
misfiled by a six-letter rule, and a store may hold pairs for
currencies its reference data never mentions.
instrument_identifiers
property
¶
Market identifiers that are instruments rather than currency pairs.
What a universe or a search should offer: a pair is a rate series, not something anybody holds.
reference_identifiers
property
¶
Unique identifiers in the reference data, or None if not loaded.
reference_columns
property
¶
Column names in the reference data, or None if not loaded.
date_range
property
¶
(earliest, latest) timestamps in the market data.
corporate_actions
property
¶
The action history. Empty rather than None when none was loaded.
features
property
¶
The feature table. Empty rather than None when none was loaded.
Exposed for persistence and for discovery, on the same terms as
market. Point-in-time reads go through fetch_features (BN-135),
not through this.
market
property
¶
The market-data container itself.
Exposed for persistence (beacon.data.store): writing a fetcher to
disk means reading back everything it holds, and the summarising
properties above cannot reconstruct a frame. Query through
fetch_market_data instead — this is the whole dataset, not an
answer to a question.
source
property
¶
Where this data was loaded from, or None if nothing recorded it.
Describes the load, not every row: a later sync merges rows from somewhere else without changing where the store came from. Modelling mixed provenance would need a source per row, which nothing asks for.
reference
property
¶
The reference-data container, or None if none was loaded.
Exposed for persistence, on the same terms as :attr:market.
resolve_session ¶
The market session date resolves to, backfilling inside the data.
A date the data has no bar for but which sits inside its coverage is a day the market was shut. The last session on or before it is the one that was actually in force through the closure — reading it is not an approximation, it is what the day was. Past the last bar nothing is known, so that answers None rather than a stale print wearing a current date; the bound is the data's own coverage rather than a day count, because no day count can tell a long closure from the unknown future.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
date
|
str | Timestamp
|
The date asked about. |
required |
Returns:
| Type | Description |
|---|---|
Timestamp | None
|
pd.Timestamp | None: The session, or None when date falls |
Timestamp | None
|
outside the data's coverage on either side. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_feature ¶
fetch_feature(
identifier: str,
field: str,
date: str | Timestamp | None = None,
feature_type: str | None = None,
max_age_days: int | None = MAX_AGE_DAYS,
) -> float | None
One feature value, as it was knowable on a date.
The point-in-time read. A value published after date is invisible,
which is what keeps a backtest from screening on numbers nobody had.
Returns:
| Type | Description |
|---|---|
float | None
|
float | None: The value, or None when nothing is knowable. An |
float | None
|
instrument with no coverage is an ordinary answer, not an error — |
float | None
|
most datasets cover most names most of the time and not all of |
float | None
|
them all of it. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_features ¶
fetch_features(
identifiers: list[str],
fields: list[str],
date: str | Timestamp | None = None,
feature_type: str | None = None,
max_age_days: int | None = MAX_AGE_DAYS,
) -> dict[str, dict[str, float | None]]
Several features for several instruments, on one date.
The batch form, on the same argument the reference batch endpoint made: a client that has to fan out per name will, and moving the fan-out inside the server only relocates the cost.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, dict[str, float | None]]
|
identifier -> field -> value. Every requested pair is |
dict[str, dict[str, float | None]]
|
present, null where nothing is knowable, so a caller reads a value |
|
dict[str, dict[str, float | None]]
|
rather than testing for a key. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
replace_features ¶
Swap the feature table, stamping the refresh.
The only mutating method on a fetcher, and it exists because an import
has to land somewhere the next request can see. It replaces rather
than edits: merged_with builds the new table, so a read in flight
keeps the frame it started with instead of watching rows appear under
it.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
feature_types ¶
feature_fields ¶
Fields the loaded features carry, optionally within one dataset.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
record_origin ¶
Note where this fetcher's data was loaded from.
delisting_dates ¶
The last date each identifier is listed, for those whose life ends.
Resolved in one pass rather than per identifier per day: an index over five thousand names and ten years would otherwise make twelve million point-in-time lookups to find a few hundred delistings.
A name is treated as still listed if any of its records is
open-ended, which is checked before taking the maximum -- max over a
column containing NaT would silently ignore the open record and retire
a name that never left.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, Timestamp]
|
identifier -> last listed date. Names that never leave are |
dict[str, Timestamp]
|
absent, so an empty mapping means a constant universe and callers |
|
dict[str, Timestamp]
|
can skip the work entirely. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
record_refresh ¶
Note that a dataset has just been refreshed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset
|
str
|
MARKET_DATASET or REFERENCE_DATASET. |
required |
when
|
datetime | None
|
The moment. None uses now, which is what a real sync wants; tests pass an explicit time so an age can be asserted rather than approximated. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the dataset is not one this fetcher holds. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
last_refreshed ¶
When a dataset was last loaded or synced.
Returns:
| Type | Description |
|---|---|
datetime | None
|
datetime or None: The moment, or None when the dataset is not |
datetime | None
|
loaded at all — which is a different statement from "loaded and |
datetime | None
|
never refreshed" and should not be collapsed into it. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
age_seconds ¶
How long ago a dataset was last refreshed, in seconds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset
|
str
|
Which dataset. |
required |
now
|
datetime | None
|
The reference moment, for tests. |
None
|
Returns:
| Type | Description |
|---|---|
float | None
|
float or None: The age, or None when the dataset is not loaded. |
float | None
|
Never negative: a clock adjustment between the two readings would |
float | None
|
otherwise report data refreshed in the future, which is noise |
float | None
|
rather than information. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
merge_market_data ¶
Fold freshly ingested rows into the market data.
Newly fetched rows win where they overlap an existing identifier and date. A re-sync of a window is a correction — a restated close, a backfilled volume — so keeping the older value would make the sync pointless.
The swap at the end is a single assignment, so a reader either sees the whole old dataset or the whole new one. This process is single-threaded and cooperatively scheduled, so there is no torn state to guard against; a reader that started before the swap simply finishes against the data it began with.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frame
|
DataFrame
|
Long-form rows carrying |
required |
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
Rows added, counting only genuinely new identifier/date pairs |
int
|
— a re-sync that restates existing rows returns 0, which is the |
|
int
|
truthful answer to "how much did this add". |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
merge_reference_data ¶
Fold freshly ingested reference records in.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frame
|
DataFrame
|
Rows carrying |
required |
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
Records added. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_corporate_actions ¶
fetch_corporate_actions(
identifier: str,
start_date: str | Timestamp | None = None,
end_date: str | Timestamp | None = None,
types: list[str] | None = None,
) -> pd.DataFrame
Corporate actions for one identifier over a window.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str
|
The instrument. |
required |
start_date
|
str | Timestamp | None
|
Earliest ex-date, inclusive. |
None
|
end_date
|
str | Timestamp | None
|
Latest ex-date, inclusive. |
None
|
types
|
list[str] | None
|
Restrict to these action types. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Matching actions, oldest first; empty when there are |
DataFrame
|
none. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_trailing_dividend ¶
Ordinary dividends per share over the trailing twelve months.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_trailing_dividend_yield ¶
fetch_trailing_dividend_yield(
identifier: str,
as_of: str | Timestamp,
price: float | None = None,
) -> float | None
Trailing dividend yield, priced off the market data by default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str
|
The instrument. |
required |
as_of
|
str | Timestamp
|
End of the trailing window. |
required |
price
|
float | None
|
Price to divide by. None reads the close on or before as_of from the market data. |
None
|
Returns:
| Type | Description |
|---|---|
float | None
|
float or None: The yield, or None when no price is available — a |
float | None
|
missing price is a reason to say nothing rather than to guess. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_market_data ¶
fetch_market_data(
identifier: str | list[str],
start_date: str | None = None,
end_date: str | None = None,
columns: list[str] | None = None,
) -> pd.DataFrame
Fetch time-series market data for one or more identifiers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str | list[str]
|
One identifier or a list of identifiers. |
required |
start_date
|
str | None
|
Date string to filter the start of the date range. |
None
|
end_date
|
str | None
|
Date string to filter the end of the date range. |
None
|
columns
|
list[str] | None
|
Subset of columns to return. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
DataFrame
|
pd.DataFrame: Single identifier: indexed by |
|
identifiers |
DataFrame
|
MultiIndexed by |
DataFrame
|
DataFrame if no matching data is found. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
warm_session ¶
Read one session's rows for identifiers in a single slice.
A hint, not a contract: every read this serves answers identically without it, only slower. What it removes is the shape a methodology walking a universe otherwise has — one frame slice per name per column, each costing what the whole frame costs rather than what one row does, which is why a preview's cost per name climbed with the size of its universe (BN-190).
It also ends the duplication that made the same names priced twice in one rebalance. A selection rule prices every candidate; the weighting scheme then prices the survivors, the same names on the same day. The second warm is a subset of the first, so it keeps the panel rather than rebuilding it, and the reads that follow are free.
Nothing goes stale under it: the panel answers only for the identifiers it was built with, only on its own session, and a merge clears it. Calling this with a different session or a name it does not hold replaces it, so the caller never has to say when it is done.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifiers
|
list[str]
|
The instruments about to be read one at a time. |
required |
date
|
str | Timestamp
|
The session they will be read on. Resolve it first —
:meth: |
required |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_shares_outstanding ¶
fetch_shares_outstanding(
identifier: str,
date: str,
column: str = "SHARES_OUTSTANDING",
) -> float | None
Return shares outstanding for identifier on date.
Sourced from the column market-data field. Returns None if the
column is not present or there is no value on that date.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_free_float_factor ¶
Return the free-float factor in force for identifier on date.
That day's value when there is one. Otherwise the last value before
it, if no older than free_float_backfill_days (BN-219): free float
moves on corporate events and reviews, so a blank cell means nothing
was reported, not that the float changed. Never a later value.
Returns None if the column is absent, or nothing was reported
within the window. Callers refuse through
:func:~beacon.data.free_float.require_free_float rather than
choosing a fallback of their own.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
prices_on ¶
:meth:fetch_price for many names on one day, in one read.
The batch face of the same answer (BN-218): warm the day's page once,
take the whole column from it, and convert NaN to None -- exactly what
fetch_price returns name by name. It exists because the daily
valuation asks the question 200 times a day, and every name paid for
a chain of four calls to reach a dict lookup.
A name the page does not answer for goes through fetch_price rather
than being assumed absent, so a panel that somehow missed it costs a
slower read instead of a wrong one.
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, float | None]
|
identifier -> price, or None where there is none. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_price ¶
Return identifier's price on date, or None if it did not print.
The scalar form of the single-day, single-name fetch a methodology
makes for every name in a universe. It reads the same value
fetch_market_data(identifier, date, date) does — the same column of
the same row — and returns it rather than a one-row frame to slice,
which is what lets a warmed session serve it (BN-190).
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_fx_rates ¶
fetch_fx_rates(
from_currency: str,
to_currency: str,
start_date: str | None = None,
end_date: str | None = None,
column: str = "RATE",
) -> pd.Series
Return the FX rate series converting from_currency into to_currency.
The pair is looked up as a market-data identifier named
f"{from_currency}{to_currency}" (upper-cased). The column field is
used if present, otherwise the first data column. Returns an empty
Series if the pair is not found.
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fx_route ¶
How a rate for this pair is found: direct, inverse or a cross.
Returns:
| Type | Description |
|---|---|
str | None
|
str | None: "direct" for a stored pair, "inverse" for one over the |
str | None
|
stored reverse pair, "cross via USD" for a rate built from two |
str | None
|
legs, "same currency" when no conversion is needed, and None when |
str | None
|
no rate can be found. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fx_rate_on ¶
The rate converting from_currency into to_currency on date.
The single currency conversion in the library (BN-188). There were three, and they disagreed: the index calculator carried a rate forward and refused when the pair was unknown, the reference endpoint substituted 1.0 and reported the local number under a dollar heading, and the market-cap weighting did not convert at all — which is how a yen name came to carry fifteen times the weight it should. One lookup means the number displayed and the number weighted by are the same quantity, which is the half of this that nothing was checking.
The series is fetched once per ordered pair and cached, because a run asks this on every foreign name on every day and each fetch slices the whole market frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
from_currency
|
str
|
The currency being converted out of. |
required |
to_currency
|
str
|
The currency being converted into. |
required |
date
|
str | Timestamp
|
The date the rate is wanted on. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float | None
|
float | None: The rate in force on date, carried forward over |
|
float | None
|
gaps, or None when the pair is unknown **or when its history |
|
float | None
|
begins after date**. Callers treat None as "cannot convert" |
|
float | None
|
rather than as a rate of one. Nothing here invents parity on a |
|
float | None
|
caller's behalf: a rate of 1.0 is a claim about two currencies, |
|
float | None
|
and the only one this makes is that a currency converts into |
|
float | None
|
itself. |
|
float | None
|
Carried forward only. A date before the series starts used to |
|
float | None
|
answer with the series' first rate — a rate dated after the day it |
|
float | None
|
was applied to, which is look-ahead (BN-204). |
|
float | None
|
states the same rule for sessions and is where the wording comes |
|
from |
float | None
|
there is no earlier observation to be in force, so there is |
float | None
|
no answer rather than a substitute for one. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 | |
latest_rows ¶
latest_rows(
identifiers: list[str],
as_of: Timestamp,
columns: list[str] | None = None,
recent_days: int = RECENT_DAYS,
) -> pd.DataFrame
One row per name: its most recent bar at or before as_of.
The batch "what is the last thing we know about these names" read, shared by the reference endpoint and the staleness gate rather than written twice (BN-211).
Two stages, because the obvious version is thirty times slower. Measured over 500 names and ten years of daily bars: reading the recent window costs 650 ms and reading the whole history costs 20.6 seconds. The fetch is not what differs -- identifier selection dominates it either way -- it is that every per-name slice afterwards then cuts a 1.37-million-row frame. So the recent window is read first and answers almost every name, and only the stragglers are read again without a lower bound.
Then the frame is reduced to one row per name once, with a grouped tail, rather than sliced per name downstream. That is what makes even an all-stale store cheap: 782 ms against 19.8 seconds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifiers
|
list[str]
|
Names to look up. |
required |
as_of
|
Timestamp
|
The date to look back from, inclusive. |
required |
columns
|
list[str] | None
|
Columns to read, or None for all of them. |
None
|
recent_days
|
int
|
How far back the cheap first stage reaches. |
RECENT_DAYS
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: MultiIndexed as the market data is, holding at most |
DataFrame
|
one row per identifier. Names with no bar at or before as_of are |
DataFrame
|
absent rather than present-and-empty. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
last_priced_on ¶
When each name last printed a bar at or before as_of.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifiers
|
list[str]
|
Names to look up. |
required |
as_of
|
Timestamp
|
The date to look back from. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, Timestamp]
|
identifier -> the date of its last bar. A name with no bar |
dict[str, Timestamp]
|
at all is absent from the mapping, which is a different thing from |
|
dict[str, Timestamp]
|
one whose last bar is old. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
stale_identifiers ¶
Which names have not traded recently enough to be worth holding.
Empty when no threshold is set, which is the default: staleness is something an installation opts into, and until it does this costs one comparison and reads nothing (BN-211).
A name with no price at all is not reported here. That is a different condition with a different remedy -- the weighting already refuses it by name -- and folding the two together would quietly excuse a missing instrument as a quiet one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifiers
|
list[str]
|
Names to test. |
required |
as_of
|
Timestamp
|
The date staleness is measured from. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
set |
set[str]
|
Identifiers whose last bar is older than the threshold. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fx_rates_on ¶
:meth:fx_rate_on over many days at once, as a Series.
The vectorised face of the same rule, for a caller that needs a rate for every day of a run rather than one date (BN-207). Chained levels want exactly that, and asking per day would be one search per day per currency where a single reindex answers the lot.
It exists so that sharing the rule does not force one call shape on every caller: the policy, the carry semantics and the meaning of "no rate" are decided here once, and the two methods differ only in how many answers they return. Two implementations of the lookup is how the library came to have five of them.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
from_currency
|
str
|
The currency being converted out of. |
required |
to_currency
|
str
|
The currency being converted into. |
required |
days
|
Index
|
The dates wanted, ascending. |
required |
Returns:
| Type | Description |
|---|---|
Series | None
|
pd.Series | None: One rate per day, indexed by days, or None when |
Series | None
|
the pair is unknown entirely. Individual days the policy cannot |
Series | None
|
answer for are NaN — a day is missing, not the pair — which is the |
Series | None
|
distinction |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_reference_data ¶
fetch_reference_data(
identifier: str | list[str],
date: str | None = None,
columns: list[str] | None = None,
) -> pd.DataFrame
Fetch reference data for one or more identifiers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str | list[str]
|
One identifier or a list of identifiers. |
required |
date
|
str | None
|
Point-in-time date. Only rows valid at this date are returned. |
None
|
columns
|
list[str] | None
|
Subset of columns to return. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Indexed by |
DataFrame
|
reference data is loaded or identifier is not found. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_classification ¶
fetch_classification(
identifier: str,
date: str | Timestamp | None = None,
scheme: str = DEFAULT_SCHEME,
) -> str | None
One instrument's classification as it stood on a date.
Reference data already carries validity ranges, so a name that moved from Industrials to Technology has two rows and this returns whichever was in force. That matters for anything historical: attributing a 2021 return to a sector the company only joined in 2023 is a real way to get a breakdown wrong.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str
|
The instrument. |
required |
date
|
str | Timestamp | None
|
The as-of date. None takes the currently-active record — the one with no end date — falling back to the latest start date if every record has been closed off. |
None
|
scheme
|
str
|
Which column to read, e.g. |
DEFAULT_SCHEME
|
Returns:
| Type | Description |
|---|---|
str | None
|
str or None: The classification, or None when it is unknown: no |
str | None
|
reference data, no such instrument, no such column, or no record |
str | None
|
valid on that date. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
fetch_classifications ¶
fetch_classifications(
identifiers: list[str],
date: str | Timestamp | None = None,
scheme: str = DEFAULT_SCHEME,
) -> dict[str, str | None]
Classifications for several instruments at once.
Every identifier appears, with None where the classification is unknown, so a caller can see what is missing rather than finding it silently absent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifiers
|
list[str]
|
The instruments. |
required |
date
|
str | Timestamp | None
|
As-of date, as for :meth: |
None
|
scheme
|
str
|
Which column to read. |
DEFAULT_SCHEME
|
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, str | None]
|
Identifier to classification. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
group_by_classification ¶
group_by_classification(
identifiers: list[str],
date: str | Timestamp | None = None,
scheme: str = DEFAULT_SCHEME,
) -> dict[str, list[str]]
Instruments grouped by classification, ready for GroupBounds.
Unclassified instruments are collected under UNCLASSIFIED rather than dropped. A name missing from every bucket is how a constraint set quietly stops covering part of the universe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifiers
|
list[str]
|
The instruments. |
required |
date
|
str | Timestamp | None
|
As-of date. |
None
|
scheme
|
str
|
Which column to read. |
DEFAULT_SCHEME
|
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, list[str]]
|
Classification to the identifiers carrying it, each list in |
dict[str, list[str]]
|
the order the identifiers were given. |
Source code in build/cache/py-beacon-2c9c3936c65abdb6b8403c50a023f58355be30ee/src/beacon/data/fetcher.py
load_data ¶
Read data files from the environment config and return a DataFetcher.
For each dataset, a DataFrame is checked first; if not provided, the file path is used instead. Raises ValueError if no market data is available from either source.