Selic / COPOM
Taxa Selic
selic — Dados e análises relacionados à taxa Selic e política monetária.
Submódulos
copom Calendário de reuniões do COPOM (BCB). compromissada Leilões de operações compromissadas do BCB. cpm Dados brutos de contratos CPM (opções digitais de COPOM) da B3. probabilities Probabilidades implícitas de mudança de meta nas reuniões do COPOM.
over(data)
Taxa SELIC Over para uma data específica.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DateLike
|
Data da consulta. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Taxa SELIC Over em decimal ou |
Examples:
Source code in pyield/bc/sgs.py
over_serie(inicio=None, fim=None, *, ultimos=None)
Taxa SELIC Over (série SGS 1178).
Taxa de juros média diária praticada no mercado interbancário, com títulos públicos como garantia.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inicio
|
DateLike | None
|
Data inicial. |
None
|
fim
|
DateLike | None
|
Data final. Se |
None
|
ultimos
|
int | None
|
Número de registros mais recentes a retornar.
Mutuamente exclusivo com |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame com colunas data e taxa (decimal), ou DataFrame vazio. |
Examples:
>>> import pyield as yd
>>> # Copom de 29-01-2025: over subiu de 12,15% para 13,15% a.a.
>>> yd.selic.over_serie("28-01-2025", "31-01-2025")
shape: (4, 2)
┌────────────┬────────┐
│ data ┆ taxa │
│ --- ┆ --- │
│ date ┆ f64 │
╞════════════╪════════╡
│ 2025-01-28 ┆ 0.1215 │
│ 2025-01-29 ┆ 0.1215 │
│ 2025-01-30 ┆ 0.1315 │
│ 2025-01-31 ┆ 0.1315 │
└────────────┴────────┘
Source code in pyield/bc/sgs.py
meta(data)
Taxa SELIC Meta para uma data específica.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DateLike
|
Data da consulta. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Taxa SELIC Meta em decimal ou |
Examples:
Source code in pyield/bc/sgs.py
meta_serie(inicio=None, fim=None, *, ultimos=None)
Taxa SELIC Meta (série SGS 432).
Taxa de juros oficial definida pelo COPOM.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inicio
|
DateLike | None
|
Data inicial. |
None
|
fim
|
DateLike | None
|
Data final. Se |
None
|
ultimos
|
int | None
|
Número de registros mais recentes a retornar.
Mutuamente exclusivo com |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame com colunas data e taxa (decimal), ou DataFrame vazio. |
Examples:
>>> import pyield as yd
>>> # Copom de 29-01-2025: meta subiu de 12,25% para 13,25% a.a.
>>> yd.selic.meta_serie("28-01-2025", "31-01-2025")
shape: (4, 2)
┌────────────┬────────┐
│ data ┆ taxa │
│ --- ┆ --- │
│ date ┆ f64 │
╞════════════╪════════╡
│ 2025-01-28 ┆ 0.1225 │
│ 2025-01-29 ┆ 0.1225 │
│ 2025-01-30 ┆ 0.1325 │
│ 2025-01-31 ┆ 0.1325 │
└────────────┴────────┘
Source code in pyield/bc/sgs.py
Calendário COPOM
copom — COPOM meeting calendar.
Past meetings sourced from the BCB public API (atas endpoint). Future meetings for the current cycle are hardcoded from the official BCB public note and must be updated each January.
BCB API field mapping
nroReuniao → MeetingNumber (sequential BCB number)
dataReferencia → EndDate (last day of the 2-day meeting)
StartDate derived as EndDate − 1 calendar day (always 2-day meetings)
calendar(start=None, end=None)
Return the full COPOM meeting calendar (past + future).
Past meetings are fetched live from the BCB API. Future meetings come from the hardcoded annual constant. Duplicates between the two sources are removed by deduplication on EndDate, so there is no need to manually keep the lists in sync.
Parameters
start, end : DateLike | None Optional inclusive date range filter applied to EndDate. None means no bound.
Returns
pl.DataFrame Columns: MeetingNumber : Int32 BCB sequential number (null for future) StartDate : Date first day of the 2-day meeting EndDate : Date last day of the 2-day meeting ExpiryDate : Date next Brazilian business day after EndDate (= B3 CPM contract settlement/expiry date) Rows are sorted by EndDate ascending.
Notes
ExpiryDate is computed with du.deslocar_expr("EndDate", 1), using
the Brazilian holiday calendar already embedded in pyield.du.
Examples:
>>> import pyield as yd
>>> cal = yd.selic.copom.calendar()
>>> "ExpiryDate" in cal.columns
True
>>> cal["EndDate"].is_sorted()
True
Source code in pyield/selic/copom.py
next_meeting(reference=None)
Return the single next COPOM meeting on or after reference.
If reference is None, today's date (Brazil timezone) is used.
Returns a one-row DataFrame with the same schema as :func:calendar.
Examples:
Source code in pyield/selic/copom.py
Compromissadas BCB
Módulo para consulta dos leilões de operações compromissadas (repos) realizados pelo BCB.
Fonte oficial (API OData): https://olinda.bcb.gov.br/olinda/servico/leiloes_selic/versao/v1/aplicacao#!/recursos/leiloes_compromissadas
Exemplo de chamada bruta (CSV): https://olinda.bcb.gov.br/olinda/servico/leiloes_selic/versao/v1/odata/leiloes_compromissadas(dataLancamentoInicio=@dataLancamentoInicio,dataLancamentoFim=@dataLancamentoFim,horaInicio=@horaInicio,dataLiquidacao=@dataLiquidacao,dataRetorno=@dataRetorno,publicoPermitidoLeilao=@publicoPermitidoLeilao,nomeTipoOferta=@nomeTipoOferta)?@dataLancamentoInicio='2025-08-21'&@dataLancamentoFim='2025-08-21'&$format=text/csv
Exemplo (CSV original): id , dataMovimento, horaInicio, publicoPermitidoLeilao, numeroComunicado, nomeTipoOferta , ofertante , prazoDiasCorridos, dataLiquidacao, dataRetorno, volumeAceito, taxaCorte, percentualCorte ac1b013d13d6fb1d9d9e251b800010ee, 2025-08-21 , 09:00 , SomenteDealer , null , Tomador , Banco Central, 1, 2025-08-21 , 2025-08-22 , 647707406, "14,9" , 0 ac1b013d13d6fb1d9d9e251b8000121e, 2025-08-21 , 12:00 , TodoMercado , 43716 , Compromissada 1047, Banco Central, 91, 2025-08-22 , 2025-11-21 , 5000000, "99,78" , "64,13"
compromissadas(inicio=None, fim=None)
Consulta e retorna leilões de operações compromissadas do BCB.
Semântica dos parâmetros de período (API OData): - inicio somente: dados de inicio até o fim da série. - fim somente: dados do início da série até fim. - ambos omitidos: série histórica completa.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inicio
|
DateLike | None
|
Data inicial (inclusive) ou None. |
None
|
fim
|
DateLike | None
|
Data final (inclusive) ou None. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
DataFrame com colunas normalizadas em português e tipos |
DataFrame
|
enriquecidos (frações decimais, inteiros, datas). |
Output Columns
- data_leilao (Date): data de ocorrência do leilão.
- data_liquidacao (Date): data de liquidação (início da operação).
- data_retorno (Date): data de recompra / término da operação.
- hora_inicio (Time): horário de início do leilão.
- prazo_dc (Int64): dias corridos até a data de retorno.
- prazo_du (Int64): dias úteis entre liquidação e retorno.
- comunicado (Int64): número do comunicado/aviso do BC (pode ser nulo).
- tipo_oferta (String): classif. do tipo de oferta (ex: Tomador, Compromissada 1047).
- publico (String): público permitido no leilão (SomenteDealer, TodoMercado).
- financeiro_aceito (Float64): financeiro aceito no leilão em reais (convertido de milhares).
- taxa_corte (Float64): taxa de corte (ex. 0.1490 = 14,90%). Nula se financeiro_aceito = 0.
- pct_aceito (Float64): percentual do volume ofertado efetivamente aceito (0-100). 100 = nenhuma rejeição. 0 indica nada aceito (volume_aceito = 0).
Notes
- Dados ordenados por: data_leilao, hora_inicio, tipo_oferta.
Examples:
>>> import polars as pl
>>> _ = pl.Config.set_tbl_width_chars(210)
>>> _ = pl.Config.set_tbl_cols(-1)
>>> import pyield as yd
>>> yd.selic.compromissadas(inicio="21-08-2025", fim="21-08-2025")
shape: (2, 12)
┌─────────────┬─────────────────┬──────────────┬─────────────┬──────────┬──────────┬────────────┬────────────────────┬───────────────┬───────────────────┬────────────┬────────────┐
│ data_leilao ┆ data_liquidacao ┆ data_retorno ┆ hora_inicio ┆ prazo_dc ┆ prazo_du ┆ comunicado ┆ tipo_oferta ┆ publico ┆ financeiro_aceito ┆ taxa_corte ┆ pct_aceito │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ date ┆ date ┆ date ┆ time ┆ i64 ┆ i64 ┆ i64 ┆ str ┆ str ┆ f64 ┆ f64 ┆ f64 │
╞═════════════╪═════════════════╪══════════════╪═════════════╪══════════╪══════════╪════════════╪════════════════════╪═══════════════╪═══════════════════╪════════════╪════════════╡
│ 2025-08-21 ┆ 2025-08-21 ┆ 2025-08-22 ┆ 09:00:00 ┆ 1 ┆ 1 ┆ null ┆ Tomador ┆ SomenteDealer ┆ 6.4771e11 ┆ 0.149 ┆ 100.0 │
│ 2025-08-21 ┆ 2025-08-22 ┆ 2025-11-21 ┆ 12:00:00 ┆ 91 ┆ 64 ┆ 43716 ┆ Compromissada 1047 ┆ TodoMercado ┆ 5.0000e9 ┆ 0.9978 ┆ 35.87 │
└─────────────┴─────────────────┴──────────────┴─────────────┴──────────┴──────────┴────────────┴────────────────────┴───────────────┴───────────────────┴────────────┴────────────┘
>>> _ = pl.Config.restore_defaults()
>>> _ = pl.Config.set_tbl_width_chars(150)
Source code in pyield/selic/compromissada.py
Contratos CPM
CPM — B3 COPOM Digital Option contract data.
Ticker format: CPM{month_code}{year2}{C|P}{strike_6digits} Example: CPMZ25C099500 ^^^ → prefix "CPM" ^ → month code "Z" = December ^^ → year "25" = 2025 ^ → option type "C" = call ^^^^^^ → strike "099500" → 99.500
Strike interpretation (Selic Meta context): strike_float = int(strike_6digits) / 1000 # e.g. 99.500 change_bps = round((strike_float - 100) * 100) # e.g. -50 bps
Month codes (B3 standard future convention): F=1, G=2, H=3, J=4, K=5, M=6, N=7, Q=8, U=9, V=10, X=11, Z=12
Implementation note
CPM options expire on the first business day AFTER the COPOM meeting ends (ExpiryDate), not on the first business day of the meeting month. This module bypasses the generic B3 pipeline (which applies a 6-char ticker filter and a DaysToExp > 0 filter, both incorrect for CPM) and calls the lower-level price-report helpers directly.
ExpiryDate and MeetingEndDate are resolved by joining against the COPOM calendar (bc.copom.calendar()) on meeting month + year extracted from the ticker. The join is a left join so CPM rows are never dropped even if the calendar has gaps for very recently announced future meetings.
data(date)
Fetch B3 end-of-day CPM contract data for a given trade date.
Parameters
date : DateLike Trade date. Accepts DD-MM-YYYY, YYYY-MM-DD, datetime.date, etc.
Returns
pl.DataFrame Columns: TradeDate : date TickerSymbol : str MeetingEndDate : date actual COPOM meeting end date (from BCB calendar) ExpiryDate : date next business day after MeetingEndDate (= B3 CPM contract settlement date) OptionType : str "call" or "put" StrikeChangeBps: int change in bps vs 100.000 strike SettlementPrice: float B3 official "Preço de Referência" from the CSV endpoint, in points (0–100). This is the price shown on the B3 dashboard ("Probabilidades da Taxa Selic Meta"). Null for older dates (> ~1 month) where the CSV endpoint is unavailable. BDaysToExp : int business days from TradeDate to ExpiryDate
Returns empty DataFrame with correct schema if no CPM data
exists for the requested date (weekend, holiday, etc.).
Examples:
>>> import pyield as yd
>>> df = yd.selic.cpm.data("29-01-2025")
>>> df.is_empty() or set(df.schema.keys()) >= {
... "data_referencia",
... "codigo_negociacao",
... "data_fim_reuniao",
... "data_expiracao",
... "tipo_opcao",
... "variacao_strike_bps",
... "preco_ajuste",
... }
True
Source code in pyield/selic/cpm.py
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 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | |
Probabilidades Implícitas
probabilities — Implied COPOM meeting probabilities from CPM option prices.
The CPM contract is a cash-or-nothing European option. Under risk-neutral pricing, the B3 settlement price in points (0–100) encodes the market-implied probability of each Selic change scenario, discounted by the DI rate to expiry (B3 Pricing Manual §3.5).
Probability conventions
RawProb = SettlementPrice * DiscountExp / 100
Direct risk-neutral probability per B3 Manual §3.5 (inverted):
p_n(K) = PR_n * exp(+n * r_n) / N
where
PR_n = SettlementPrice (B3 "Preço de Referência", points 0–100)
N = 100 (fixed notional)
n = BDaysToExp / 252 (time in years, business-day convention)
r_n = ln(1 + DI1Rate) (continuously-compounded DI1 rate to expiry)
DI1Rate = flat-forward interpolated DI1 rate from TradeDate to ExpiryDate
SettlementPrice in cpm.data() is the B3 official "Preço de Referência"
from the B3 CSV endpoint — the price shown on the B3 dashboard
("Probabilidades da Taxa Selic Meta") and the output of B3's
P1/P2/P3/P4 methodology. It may be null for dates older than ~1 month
where the CSV endpoint is unavailable.
May not sum to 1.0 per meeting due to bid-ask spreads or B3 P1/P2 pricing.
When BDaysToExp == 0 (meeting-day itself), DiscountExp == 1.0 exactly and
RawProb reduces to SettlementPrice / 100.
Prob = RawProb / sum(RawProb) within ExpiryDate group Normalized so each meeting sums to exactly 1.0. This is the B3 P3 pricing adjustment. Use this for scenario analysis and charts.
CumProb = cumulative sum of Prob, sorted by StrikeChangeBps ascending.
Notes on null SettlementPrice
CPM contracts are sometimes listed without a settlement price (no B3 official pricing for that strike on that date, or CSV endpoint unavailable for older dates). Strikes with null SettlementPrice are excluded from the probability output because:
- Their contribution to the normalized distribution is undefined.
- Polars
group_by().agg(sum())returns 0.0 (not null) for all-null groups, which would break the invariantProb.sum() == 1.0per meeting.
As a consequence, a meeting where ALL listed strikes have null prices (e.g. CPMH25 on the January 2025 COPOM day) will not appear in the output. MeetingRank is therefore assigned over the priced meetings only and is always a consecutive sequence [1, 2, …, n].
Notes on DI1 fallback
If DI1 data is unavailable for the trade date (network error, holiday, etc.), DI1Rate falls back to 0.0 and DiscountExp to 1.0, so RawProb reduces to SettlementPrice / 100 — equivalent to the old (incorrect) formula. This degradation is logged as a warning but never raises an exception.
all_meetings(date, option_type='call')
Implied COPOM probabilities for every meeting with CPM contracts
trading on date.
Only strikes with a non-null SettlementPrice are included. Meetings where all listed strikes have null prices are excluded entirely (see module-level notes).
Parameters
date : DateLike Trade date. option_type : {"call", "put"} Which side to use. Default "call" (the liquid side in practice).
Returns
pl.DataFrame Columns: TradeDate : Date MeetingEndDate : Date ExpiryDate : Date MeetingRank : Int32 1 = nearest meeting with priced contracts StrikeChangeBps: Int32 sorted ascending within each meeting BDaysToExp : Int32 business days from TradeDate to ExpiryDate SettlementPrice: Float64 B3 "Preço de Referência" in points (0–100) DI1Rate : Float64 flat-forward DI1 rate to ExpiryDate DiscountExp : Float64 exp(BDaysToExp/252 * ln(1+DI1Rate)) RawProb : Float64 SettlementPrice * DiscountExp / 100 Prob : Float64 normalized, sums to 1.0 per meeting CumProb : Float64 cumulative Prob ascending by strike
Sorted by (MeetingRank, StrikeChangeBps).
Returns empty DataFrame with correct schema on missing data.
Examples:
>>> import pyield as yd
>>> import polars as pl
>>> df = yd.selic.probabilities.all_meetings("29-01-2025")
>>> df.is_empty() or df["ranking_reuniao"].min() == 1
True
>>> sums = df.group_by("data_expiracao").agg(pl.col("prob").sum())
>>> df.is_empty() or (sums["prob"] - 1.0).abs().max() < 1e-9
True
Source code in pyield/selic/probabilities.py
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 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | |
meeting(date, expiration=None, option_type='call')
Implied COPOM probabilities for a single meeting.
Parameters
date : DateLike Trade date. expiration : DateLike | None ExpiryDate of the target meeting (B3 contract expiry date, i.e. next business day after the meeting end). If None, the nearest meeting with priced contracts is used. option_type : {"call", "put"} Which side to use. Default "call".
Returns
pl.DataFrame Same schema as all_meetings(), filtered to the single meeting. MeetingRank is always 1 in this output (relative to the selected meeting — do not confuse with rank across all meetings).
Examples:
>>> import pyield as yd
>>> df = yd.selic.probabilities.meeting("29-01-2025")
>>> df.is_empty() or abs(df["prob"].sum() - 1.0) < 1e-9
True
>>> df.is_empty() or df["prob_acumulada"].tail(1).item() == 1.0
True