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

  1. Rename the field to end with `_q` (quarterly) or `_a` (annual), e.g. `$$roewa_q`.
  2. 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

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


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/7e5959d21fccf99f. Report an issue: GitHub.