2025, Oct 19 16:16

CALENDAR_NON_SETTLEMENT_DATES в Bloomberg xbbg: правильный вызов blp.bds

Как в Bloomberg xbbg вызвать blp.bds для CALENDAR_NON_SETTLEMENT_DATES: использовать валютный тикер, передать overrides и получить будущие несеттлмент‑дни.

Планировать торговые системы с учётом несеттлмент‑дней возможно только в том случае, если заранее запрашивать у Bloomberg календарь выходных. Полагаться на blp.bdh, чтобы «по факту» обнаруживать пропуски, не получится. Цель — достать будущие неторговые дни для нужного рынка через CALENDAR_NON_SETTLEMENT_DATES и сделать это надёжно в коде.

Постановка задачи

Задумка была пройтись циклом по набору тикеров и кодов стран, затем запросить несеттлмент‑даты для расширенного окна вокруг заданного месяца. Но несмотря на разные тикеры и коды стран, результатом постоянно оказывался пустой DataFrame.

import pandas as pd
import numpy as np
from xbbg import blp
from datetime import datetime, timedelta

inst_map = {
    'SOFRRATE Index': ('USD Curncy', 'US'),
    'BISTTREF Index': ('TRY Curncy', 'TU'), 
    'MUTKCALM Index': ('JPY Curncy','JN'),
    'RUONIA Index': ('RUB Cunrcy','R$'),
    'SIBCSORA Index': ('SGD Curncy','SI'),
    'SONIO/N Index': ('GBP Curncy','GB'),
    'SRFXON3 Index': ('CHF Curncy','SZ'),
    'TTHORON Index': ('TWD Curncy','T+')
}

def slide_month(y: int, m: int, delta: int):
    new_m = m + delta
    new_y = y + (new_m - 1) // 12
    new_m = ((new_m - 1) % 12) + 1
    return new_y, new_m

def collect_blackout_days(y: int, m: int):
    s_y, s_m = slide_month(y, m, -1)
    e_y, e_m = slide_month(y, m, +1)

    s_date = datetime(s_y, s_m, 10).strftime('%Y%m%d')
    e_date = datetime(e_y, e_m, 15).strftime('%Y%m%d')

    days_by_index = {}

    for idx, (_, ctry_code) in inst_map.items():
        try:
            out = blp.bds(
                idx,
                'CALENDAR_NON_SETTLEMENT_DATES',
                [
                    f'SETTLEMENT_CALENDAR_CODE={ctry_code}',
                    f'CALENDAR_START_DATE={s_date}',
                    f'CALENDAR_END_DATE={e_date}'
                ]
            )
            holidays = out.get('calendar_non_settlement_dates', [])
            days_by_index[idx] = holidays
        except Exception as err:
            print(f"Error for {idx} : {err}")
            days_by_index[idx] = []

    return days_by_index

Что идёт не так и почему

Проблема в том, как формируется запрос к blp.bds. Во‑первых, рабочий вызов использует валютный тикер при запросе CALENDAR_NON_SETTLEMENT_DATES. Во‑вторых, параметры‑override нужно передавать как именованные аргументы, а не как список строк. Есть и важная деталь из исходников библиотеки: третий позиционный аргумент не предназначен для списка — поэтому передача списка и даёт пустой ответ. В итоге сочетание неподходящего тикера и неверного формата overrides приводит к пустым DataFrame.

Рабочий подход

Корректный вызов использует валютный тикер и именованные overrides, задающие код календаря и диапазон дат. На выходе — DataFrame со столбцом holiday_date, где даты идут в формате yyyy-mm-dd.

from xbbg import blp

holidays = blp.bds(
    'USD Curncy',
    'CALENDAR_NON_SETTLEMENT_DATES',
    SETTLEMENT_CALENDAR_CODE='FD',
    CALENDAR_START_DATE='20250101',
    CALENDAR_END_DATE='20261231'
)

print(holidays)

Примечание: FD — это календарь для США.

Зачем это важно

Несеттлмент‑дни влияют на расписание прогонов ценообразования, движение средств, логику роллов и операционные процессы. Если заранее запрашивать календарь у Bloomberg — до наступления дат — можно синхронизировать процедуры, избежать ложных тревог из‑за «пропусков» и удерживать стабильность смежных систем.

Выводы

Если нужны будущие торговые выходные, запрашивайте CALENDAR_NON_SETTLEMENT_DATES по корректному инструменту и передавайте overrides именованными аргументами. Разбираясь с пустым DataFrame, проверьте сигнатуру функции и форму параметров по реализации, чтобы исключить неверный формат позиционных аргументов. Когда запрос сформирован правильно, DataFrame со значениями holiday_date можно сразу подключать к вашей логике планирования.

Статья основана на вопросе на StackOverflow от Jstne и ответе от Jstne.