microsoft/qlib · error · ValueError
period field must ends with '_q' or '_a'
Error message
period field must ends with '_q' or '_a'
What it means
PIT period fields encode their period in the suffix: `_q` means quarterly revision data, `_a` annual. `LocalPITProvider.period_feature` branches on that suffix to pick the interpretation, so any other name is meaningless to the storage layer and raises ValueError.
Source
Thrown at qlib/data/data.py:780
VALUE_DTYPE = C.pit_record_type["value"]
field = str(field).lower()[2:]
instrument = code_to_fname(instrument)
# {For acceleration
# start_index, end_index, cur_index = kwargs["info"]
# if cur_index == start_index:
# if not hasattr(self, "all_fields"):
# self.all_fields = []
# self.all_fields.append(field)
# if not hasattr(self, "period_index"):
# self.period_index = {}
# if field not in self.period_index:
# self.period_index[field] = {}
# For acceleration}
if not field.endswith("_q") and not field.endswith("_a"):
raise ValueError("period field must ends with '_q' or '_a'")
quarterly = field.endswith("_q")
index_path = C.dpm.get_data_uri() / "financial" / instrument.lower() / f"{field}.index"
data_path = C.dpm.get_data_uri() / "financial" / instrument.lower() / f"{field}.data"
if not (index_path.exists() and data_path.exists()):
raise FileNotFoundError("No file is found.")
# NOTE: The most significant performance loss is here.
# Does the acceleration that makes the program complicated really matters?
# - It makes parameters of the interface complicate
# - It does not performance in the optimal way (places all the pieces together, we may achieve higher performance)
# - If we design it carefully, we can go through for only once to get the historical evolution of the data.
# So I decide to deprecated previous implementation and keep the logic of the program simple
# Instead, I'll add a cache for the index file.
data = np.fromfile(data_path, dtype=DATA_RECORDS)
# find all revision periods before `cur_time`
cur_time_int = int(cur_time.year) * 10000 + int(cur_time.month) * 100 + int(cur_time.day)
loc = np.searchsorted(data["date"], cur_time_int, side="right")
if loc <= 0:View on GitHub (pinned to 79633dd950)
Solutions
- Rename the field to end with `_q` (quarterly) or `_a` (annual), e.g. `$$roewa_q`.
- Check the field list in your PIT data directory (`features/financial/<inst>/*.index`) for exact available names and suffixes.
Example fix
# before df = D.features(insts, ['P($$roewa)']) # after df = D.features(insts, ['P($$roewa_q)'])
Defensive patterns
Strategy: validation
Validate before calling
def is_valid_period_field(f: str) -> bool:
return f.endswith('_q') or f.endswith('_a')
assert all(is_valid_period_field(f.lstrip('P($')) for f in pit_fields) Type guard
def is_valid_period_field(f: str) -> bool:
f = f.strip()
return f.endswith('_q') or f.endswith('_a') Prevention
- Keep _q/_a suffixes when dumping PIT data with dump_bin/get_and_dump_financial_data.
- Use lowercase suffixes; the check is case-sensitive.
When it happens
Trigger: Requesting `P($$roewa)` or `P($$myfield)` where the name does not end in _q/_a; typos like `$$roewa_Q` (capital Q) also fail since the check is case-sensitive lowercase.
Common situations: Hand-writing financial field names or porting names from other data vendors; renaming fields in dumped PIT data without keeping the suffix convention.
Related errors
- {freq} is not supported in NumpyQuote
- {method} is not supported
- Invalid Qlib configuration (note: the global config has alre
- provider_uri cannot be None
- risk_analysis accumulation mode {mode} is not supported. Exp
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/7e5959d21fccf99f.
Report an issue: GitHub.