bizdays: Dias Úteis em Qualquer Calendário

Grupy-SP—julho/2015




Sobre mim

Wilson Freitas

  • quant
  • físico
  • corredor
  • padeiro

Background

  • Sistemas Complexos
  • Finanças Quantitativas
  • Matemática

Programação

  • R, Python, MATLAB, C/C++

Motivação

O mercado financeiro brasileiro possui diversas particularidas que complicam a simples tarefa de calcular os preços dos ativos financeiros.

  • liquidação de contratos de dólar com a cotação do dia anterior
  • contratos com vencimento fixo ao contrário do resto do mundo que negocia prazos fixos
  • regras absurdas para definição de vencimentos
  • e ...
  • vigência do contrato em dias úteis, para diferentes calendários
  • entre outras

Aqui vamos falar sobre como os desafios de lidar com dias úteis foram vencidos utilizando Python.

Contagem de dias úteis

  • No Brasil as taxas de juros são calculadas em dias úteis
  • Herança do período inflacionário
  • O problema é que com as taxas de juros apuradas em dias úteis força que todos os demais contratos sigam a mesma convenção:
    • contratos futuros, títulos públicos, opções
  • Diferentes mercados diferentes regras:
    • Mercado de derivativos (antiga BM&F) possui um calendário de feriados
    • Mercado de ações (antiga Bovespa) possui outro calendário
    • Contratos de moedas utilizam dias corridos

Problema: diferentes ativos precisam lidar com a contagem de dias de forma diferente

Módulo python-bizdays

História

  • A contagem em dias foi desenvolvida para o módulo python-fixedincome
  • bizdays nasceu em R em set/2013
  • Foi portado para Python em seguida

Objetivos

  • Objetivo 1: realizar operações com dias úteis!
  • Objetivo 2: fazer uma coisa tão simples de usar quanto a função NETWORKDAYS do Excel
  • Objetivo 3: Poder lidar com mais de 1 calendário sem if

bizdays in action

In [1]:
from bizdays import Calendar

holidays = ['2015-01-01', '2015-02-16', '2015-02-17', '2015-04-03', '2015-04-21',
            '2015-05-01', '2015-06-04', '2015-09-07', '2015-10-12', '2015-11-02',
            '2015-11-15', '2015-12-25', '2016-01-01']
cal = Calendar(holidays=holidays, weekdays=['Sunday', 'Saturday'])
In [2]:
# Dia do Trabalho é dia útil?
cal.isbizday('2015-05-01')
Out[2]:
False
In [3]:
# 12/07/2015 (domingo) é dia útil?
cal.isbizday('2015-07-12')
Out[3]:
False
In [4]:
# 03/08/2015 (sengunda-feira) é dia útil?
cal.isbizday('2015-08-03')
Out[4]:
True
In [5]:
# Quantidade de dias úteis no ano:
cal.bizdays('2015-01-01', '2015-12-31')
Out[5]:
249
In [6]:
# Quantidade de dias úteis até hoje:
cal.bizdays('2015-01-01', '2015-07-16')
Out[6]:
133
In [7]:
# Todos os dias úteis em um período, pex primeira quinzena de abril
list(cal.seq('2015-04-01', '2015-04-15'))
Out[7]:
[datetime.date(2015, 4, 1),
 datetime.date(2015, 4, 2),
 datetime.date(2015, 4, 6),
 datetime.date(2015, 4, 7),
 datetime.date(2015, 4, 8),
 datetime.date(2015, 4, 9),
 datetime.date(2015, 4, 10),
 datetime.date(2015, 4, 13),
 datetime.date(2015, 4, 14),
 datetime.date(2015, 4, 15)]

Cálculo de preços de ativos financeiros

  • Os ativos financeiros tem data de vencimento fixa
    • Contratos Futuros de taxas de juros (DI1) expiram no dia 01 do mês de vencimento

Use adjust_next (ou adjust_previous)

In [8]:
refdate = '2015-03-12'
maturity = '2015-05-01'
In [9]:
cal.bizdays(refdate, maturity)
Out[9]:
33
In [10]:
cal.adjust_next(maturity)
Out[10]:
datetime.date(2015, 5, 4)
In [11]:
cal.bizdays(refdate, cal.adjust_next(maturity))
Out[11]:
34

Também trabalha com vetores de datas (atributo vec :/)

In [12]:
refdate = '2015-03-12'
maturities = ['2015-05-01', '2015-06-01', '2015-08-01']
In [13]:
list(cal.vec.adjust_next(maturities))
Out[13]:
[datetime.date(2015, 5, 4),
 datetime.date(2015, 6, 1),
 datetime.date(2015, 8, 3)]
In [14]:
list(cal.vec.bizdays(refdate, list(cal.vec.adjust_next(maturities))))
Out[14]:
[34, 54, 98]

Manipulando curvas de taxas de juros

In [15]:
import pandas as pd

Carregando um calendário em um arquivo

In [16]:
!head -6 ANBIMA.cal
Saturday
Sunday
2000-01-01
2000-03-06
2000-03-07
2000-04-21
In [17]:
cal252 = Calendar.load('ANBIMA.cal')
cal360 = Calendar()

Carregando taxas de juros divulgadas pela BM&FBovespa

In [18]:
url = 'http://www2.bmf.com.br/pages/portal/bmfbovespa/boletim1/TxRef1.asp'
dfs = pd.read_html(url)
curva = dfs[2].ix[2:]
curva.columns = ['DC', 'Taxa_252', 'Taxa_360']
curva.index = range(1, len(curva)+1)
curva.dtypes
Out[18]:
DC           object
Taxa_252     object
Taxa_360    float64
dtype: object
In [19]:
curva.head()
Out[19]:
DC Taxa_252 Taxa_360
1 1 1364 1340
2 8 1368 1472
3 15 1368 1438
4 16 1368 1473
5 19 1368 1335

Separando as curvas

Criar 2 curvas uma de taxas em dias corridos e outra em dias úteis.

In [20]:
curva360 = pd.DataFrame({'DC': curva['DC'].apply(int),
                         'Taxa_360': curva['Taxa_252'].apply(lambda x: float(x)/100)})
curva360.head(3)
Out[20]:
DC Taxa_360
1 1 13.64
2 8 13.68
3 15 13.68
In [21]:
refdate = '2015-07-15'
dates = list(cal360.vec.offset(refdate, curva360['DC']))

curva252 = pd.DataFrame({'DU': list(cal252.vec.bizdays(refdate, dates)),
                         'Taxa_252': curva['Taxa_252'].apply(lambda x: float(x)/100)})
curva252.head(3)
Out[21]:
DU Taxa_252
1 1 13.64
2 6 13.68
3 11 13.68

Definindo vencimentos dos contratos

Maioria dos contratos futuros

Vencimento no primeiro dia de cada mês

In [22]:
cal.getdate('first day', 2015, 1)
Out[22]:
datetime.date(2015, 1, 1)
In [23]:
cal.getdate('1st day', 2015, 5)
Out[23]:
datetime.date(2015, 5, 1)

Contratos futuros de inflação

Vencimento do dia 15 de cada mês

In [24]:
cal.getdate('15th day', 2015, 7)
Out[24]:
datetime.date(2015, 7, 15)

Contratos futuros de soja

Vencimento nos segundos dias úteis

In [25]:
cal.getdate('2nd bizday', 2015, 5)
Out[25]:
datetime.date(2015, 5, 5)

Contratos futuros de boi gordo

Vencimento no último dia útil do mês

In [26]:
cal.getdate('last bizday', 2015, 5)
Out[26]:
datetime.date(2015, 5, 29)

Contratos futuros de café

Vencimento do sexto dia útil anterior ao último dia do mês

In [27]:
cal.getdate('6th bizday before last day', 2015, 7)
Out[27]:
datetime.date(2015, 7, 23)

Contratos futuros de índice (IBOVESPA)

Vencimento na quarta-feira mais próxima do dia 15 do mês

In [28]:
cal.getdate('first wed after 15th day', 2015, 6)
Out[28]:
datetime.date(2015, 6, 17)
In [29]:
cal.getdate('first wed before 15th day', 2015, 6)
Out[29]:
datetime.date(2015, 6, 10)