quantlab.acquisition.tiingo.TiingoAcquisition#

class quantlab.acquisition.tiingo.TiingoAcquisition(config)[source]#

Bases: SymbolBatchAcquisition

Download Tiingo end-of-day prices through the shared SymbolBatchAcquisition base.

Tiingo’s price endpoint takes one symbol per call and returns the whole requested date range in one response. So this vendor uses DEFAULT_BATCH_SIZE = 1 and _fetch_page never returns a page token; neither is a placeholder.

Beyond building the request, this class decides what an error means. A 429 from Tiingo means the account’s hourly quota is used up, which affects every symbol. It is classified as "quota", and the base class stops sending work for the whole run instead of letting every remaining symbol fail in turn. AlpacaAcquisition treats the same status as a per-minute rate limit to wait out, which is why each vendor class declares its own status codes.

Run options such as resume, max_workers, progress, batch_size, legacy_watermarks, wait_for_quota, quota_wait_seconds and quota_max_waits are read from config.kwargs, so every run is reproducible from its config file.

Credentials. TIINGO_API_KEY is read from the environment in the constructor and passed only to the in-memory TiingoClient. It is never put on the config or on any attribute that could be serialised, and its value is removed from every captured message.

Parameters:
configAcquisitionConfig

The acquisition config; frequency must be "1d".

Raises:
RuntimeError

If TIINGO_API_KEY is unset or empty.

Examples

Needs TIINGO_API_KEY exported; download() goes to the network:

from quantlab.acquisition.config import AcquisitionConfig

cfg = AcquisitionConfig(
    market="us_equity", frequency="1d", vendor="tiingo",
    raw_data_dir_path="downloads/nasdaq_data/tiingo",
    watermark_path="downloads/nasdaq_data/_watermarks/tiingo",
    symbols=("AAPL", "MSFT"), start_date="2024-01-02",
    end_date="2024-01-03",
)
acq = TiingoAcquisition(cfg).download()
print(acq.coverage_report())

Each symbol’s rows land as one parquet shard per month, under month=YYYY-MM/ beneath raw_data_dir_path, with the columns in RAW_COLUMNS.

CREDENTIAL_ENV_VARS = ('TIINGO_API_KEY',)#

The one credential the request policy hides (as <TIINGO_API_KEY REDACTED>) before a message reaches a log line or the failure manifest. Taken from the module-level constant, not from the client class tests replace.

DEFAULT_BATCH_SIZE = 1#

One symbol per request, because the endpoint takes one symbol. A larger value would loop inside _fetch_page while treating the batch as one unit: one failure would fail N symbols, the quota check would run once per N requests, and resuming would work in steps of N symbols.

QUOTA_MESSAGE_TOKEN = 'request allocation'#

The stable part of the vendor’s quota message (“You have run over your hourly request allocation. Contact us at …”). Matching the whole sentence would break as soon as the vendor rewords it. Text matching is fragile, so it is kept to this one constant.

QUOTA_STATUS_CODES = frozenset({429})#

HTTP statuses that mean the account’s quota is used up, which affects the whole run. Only 429: Tiingo also returns 403 for a single ticker the plan does not cover, and treating that as run-wide would let one ticker stop a full-market run. A 403 whose body mentions the quota is still caught by QUOTA_MESSAGE_TOKEN.

RAW_COLUMNS = ('timestamp', 'symbol', 'vendor', 'open', 'high', 'low', 'close', 'volume', 'adjOpen', 'adjHigh', 'adjLow', 'adjClose', 'adjVolume', 'divCash', 'splitFactor')#

Shard columns, in order: timestamp, symbol and vendor first, then Tiingo’s end-of-day fields in request order. vendor keeps the data’s origin visible after data from several vendors is combined.

RAW_SCHEMA = {'adjClose': Float64, 'adjHigh': Float64, 'adjLow': Float64, 'adjOpen': Float64, 'adjVolume': Float64, 'close': Float64, 'divCash': Float64, 'high': Float64, 'low': Float64, 'open': Float64, 'splitFactor': Float64, 'symbol': String, 'timestamp': Datetime(time_unit='us', time_zone=None), 'vendor': String, 'volume': Float64}#

Explicit dtype of every shard column. pl.DataFrame infers types per response, so a symbol whose divCash is all integer zero, or whose volume is all null, would get a differently typed shard. A directory scan would then fail with a SchemaError that names a file rather than the cause.

VENDOR = 'tiingo'#

Which vendor this subclass fetches from. Set by every subclass. It is written into every shard as a literal vendor column and hashed into every PageLedger.batch_key, so changing it changes the files.