DeskPricer

Microserviço local de precificação via HTTP para opções de ações europeias e americanas padrão.

Documentação

DeskPricer v3.5.0

Microserviço HTTP local de precificação para opções europeias e americanas de ações. Projetado para integração com Excel WEBSERVICE + FILTERXML — sem VBA, sem chamadas ao terminal Bloomberg dentro do serviço.

Intenção de design: DeskPricer é uma ferramenta exclusivamente local para precificação pessoal de mesa e análise de opções. Não se destina a ser executado ou servido como um serviço público/estilo servidor. Todas as escolhas de design — vinculação a localhost, sem autenticação, sem TLS, sem limitação de taxa, XML por padrão — refletem isso.


Uso com Agentes de IA (MCP)

DeskPricer está disponível como um servidor MCP. Adicione-o ao Cursor, Claude Desktop ou qualquer agente compatível com MCP:

pip install deskpricer

Publicado no PyPI: https://pypi.org/project/deskpricer/

Cursor — adicione ao ~/.cursor/mcp.json:

{
  "mcpServers": {
    "deskpricer": {
      "command": "deskpricer-mcp",
      "args": []
    }
  }
}

Claude Desktop — adicione ao claude_desktop_config.json:

{
  "mcpServers": {
    "deskpricer": {
      "command": "deskpricer-mcp"
    }
  }
}

Se deskpricer-mcp não estiver no seu PATH, use o caminho completo para o executável no seu ambiente virtual.

Ferramentas: price_option, implied_volatility, pnl_attribution, portfolio_greeks — mesmo mecanismo de precificação da API HTTP.

Consulte docs/mcp_quickstart.md para configuração completa, convenções e exemplos de prompts.


Início Rápido

Do clone limpo a uma chamada de precificação funcional em menos de 5 minutos:

# 1. Install
python -m venv .venv
.venv\Scripts\activate
pip install -e ".[dev]"

# 2. Run
python -m deskpricer.main

# 3. Test with curl
curl "http://127.0.0.1:8765/v1/greeks?s=100&k=105&t=0.25&r=0.05&q=0.02&b=0.0&v=0.20&type=call&style=european"

# 4. Test with Excel (copy into a cell)
# =FILTERXML(WEBSERVICE("http://127.0.0.1:8765/v1/greeks?s=100&k=105&t=0.25&r=0.05&q=0.02&b=0.0&v=0.20&type=call&style=european"),"//outputs/price")

Saída esperada para a chamada curl (XML):

<?xml version="1.0" encoding="UTF-8"?>
<greeks>
  <meta>
    <service_version>3.5.0</service_version>
    <quantlib_version>1.42.1</quantlib_version>
    <engine>analytic</engine>
    <valuation_date>2026-06-10</valuation_date>
  </meta>
  <inputs>
    <s>100.0</s>
    <k>105.0</k>
    <t>0.25</t>
    <r>0.05</r>
    <q>0.02</q>
    <b>0.0</b>
    <v>0.2</v>
    <type>call</type>
    <style>european</style>
  </inputs>
  <outputs>
    <price>2.288743</price>
    <delta>0.356244</delta>
    <gamma>0.037206</gamma>
    <vega>0.185519</vega>
    <theta>-0.033315</theta>
    <rho>0.083111</rho>
    <charm>-0.001241</charm>
  </outputs>
</greeks>

Para JSON, envie Accept: application/json ou acrescente ?format=json.


Experimente a Pasta de Trabalho de Demonstração

Abra sample/DeskPricer_Bitcoin_Demo.xlsx para um exemplo pronto para execução. Ela contém 3 planilhas:

PlanilhaO que mostra
GreeksOpção de Compra Europeia de Bitcoin — spot de $75K, strike de $100K, vencimento de 3M, volatilidade de 50%
ImpliedVolRecupera ~68,3% de volatilidade implícita a partir de um preço de mercado de $3.398,71
PnL AttributionDecompõe o PnL quando o spot sobe de $75K → $80K e a volatilidade se amplia de 50% → 55%

Cada planilha tem as fórmulas reais de WEBSERVICE e FILTERXML pré-carregadas. Basta iniciar o DeskPricer e as células serão preenchidas automaticamente.


O que ele faz

  • Preço + Greeks para opções individuais ou carteiras com múltiplas pernas
  • Solver de volatilidade implícita (método Brent via QuantLib)
  • Atribuição de PnL — decompõe o PnL da opção em delta, gamma, vega, theta, rho, vanna, volga e residual
  • XML por padrão — Excel WEBSERVICE + FILTERXML funcionam imediatamente; JSON disponível via Accept: application/json
  • Somente localhost — vincula-se a 127.0.0.1; sem exposição à rede

Convenções

GreekUnidade / Convenção
deltaAbsoluto (∂V/∂S)
gammaAbsoluto (∂²V/∂S²)
vegaPor ponto de volatilidade de 1%
thetaPor dia corrido (ACT/365). Negativo para opções longas típicas (decadência temporal). O sinal é oposto ao do Bloomberg DM, que reporta theta como decadência positiva.
rhoPor ponto de taxa de 1% (somente taxa livre de risco; sem rho de rendimento de dividendos ou custo de empréstimo)
charmPor dia corrido (∂delta/∂t)

Custo de empréstimo (b): Custo anualizado opcional de empréstimo de ações (decimal). O custo efetivo de carregamento é r − q − b. Omitido ou 0.0 corresponde ao comportamento anterior à versão 3.4.0.

Tempo até o vencimento (t): Fornecido em anos sob ACT/365. Internamente convertido para dias corridos (round(t * 365)) com um piso mínimo de 1 dia, depois rolado para o próximo dia útil usando o calendário escolhido (hong_kong por padrão). Theta e charm são calculados por dia corrido, não por dia útil.

Atribuição de PnL: calendar_days representa o período real decorrido de manutenção em dias corridos. theta_pnl = theta × calendar_days_elapsed. Se ambas as datas de avaliação forem omitidas, calendar_days assume o padrão de 1. Forneça datas explícitas para precisão em períodos de manutenção de vários dias.


Opções de Instalação

Executável Independente (Recomendado)

Baixe DeskPricer_v3.exe da página de Releases e execute:

.\DeskPricer_v3.exe

O serviço inicia na porta 8765. Para usar uma porta diferente:

.\DeskPricer_v3.exe --port 9000

A partir do Código-Fonte

python -m venv .venv
.venv\Scripts\activate
pip install -e ".[dev]"
python -m deskpricer.main

Guia do Usuário para Excel

Status do Serviço

Verifique se o serviço está em execução antes de obter preços:

CélulaFórmula
Status=IFERROR(FILTERXML(WEBSERVICE("http://127.0.0.1:8765/v1/health"),"//status"),"DOWN")

Saída esperada: UP


Exemplo 1: Precificar uma Única Opção + Greeks

Suponha que sua planilha tenha:

ColunaRótuloValor de Exemplo
CSpot100
KStrike105
TTempo até o vencimento (anos)0.25
RTaxa livre de risco0.05
QRendimento de dividendos0.02
BCusto de empréstimo (opcional, padrão 0,0)0.0
VVolatilidade0.20
TYPETipo de opçãocall
STYLEEstiloeuropean

Passo 1 — Construa a URL em uma célula auxiliar (ex.: H2):

="http://127.0.0.1:8765/v1/greeks?s="&C2&"&k="&K2&"&t="&T2&"&r="&R2&"&q="&Q2&"&b="&B2&"&v="&V2&"&type="&TYPE2&"&style="&STYLE2

Passo 2 — Obtenha o XML bruto (ex.: I2):

=WEBSERVICE(H2)

Passo 3 — Extraia os valores em células individuais:

SaídaFórmula
Preço=VALUE(FILTERXML(I2,"//outputs/price"))
Delta=VALUE(FILTERXML(I2,"//outputs/delta"))
Gamma=VALUE(FILTERXML(I2,"//outputs/gamma"))
Vega=VALUE(FILTERXML(I2,"//outputs/vega"))
Theta=VALUE(FILTERXML(I2,"//outputs/theta"))
Rho=VALUE(FILTERXML(I2,"//outputs/rho"))
Charm=VALUE(FILTERXML(I2,"//outputs/charm"))

Saída esperada para o exemplo acima:

GreekValor
Preço2.288743
Delta0.356244
Gamma0.037206
Vega0.185519
Theta-0.033315
Rho0.083111
Charm-0.001241

Dica: Envolva cada FILTERXML em IFERROR(...,"ERR") para que uma linha com erro não quebre a planilha inteira.


Exemplo 2: Recuperar Volatilidade Implícita a partir do Preço de Mercado

Você observa um preço médio de mercado de 6.50 para a mesma opção e deseja a volatilidade implícita.

Passo 1 — Construa a URL (ex.: H2):

="http://127.0.0.1:8765/v1/impliedvol?s="&C2&"&k="&K2&"&t="&T2&"&r="&R2&"&q="&Q2&"&b="&B2&"&price=6.50&type="&TYPE2&"&style="&STYLE2

Passo 2 — Extraia a volatilidade implícita:

=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/implied_vol"))

Saída esperada: 0.417484 (≈ 41,7% de volatilidade)


Exemplo 3: Atribuição de PnL

Você tinha uma posição ontem (t-1) e deseja explicar o PnL de hoje.

Suponha:

CampoValor em t-1Valor em t
Spot100102
Tempo0.250.2466
Vol0.200.22
Taxa0.050.05
Div0.020.02
Empréstimo0.00.0
Qtd10—

Passo 1 — Construa a URL:

="http://127.0.0.1:8765/v1/pnl_attribution?s_t_minus_1=100&s_t=102&k=105&t_t_minus_1=0.25&t_t=0.2466&r_t_minus_1=0.05&r_t=0.05&q_t_minus_1=0.02&q_t=0.02&b_t_minus_1=0.0&b_t=0.0&v_t_minus_1=0.2&v_t=0.22&type=call&style=european&qty=10&cross_greeks=true"

Passo 2 — Extraia os componentes da atribuição:

ComponenteFórmula
PnL Real=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/actual_pnl"))
PnL de Delta=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/delta_pnl"))
PnL de Gamma=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/gamma_pnl"))
PnL de Vega=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/vega_pnl"))
PnL de Theta=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/theta_pnl"))
PnL de Rho=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/rho_pnl"))
PnL de Vanna=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/vanna_pnl"))
PnL de Volga=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/volga_pnl"))
Residual=VALUE(FILTERXML(WEBSERVICE(H2),"//outputs/residual_pnl"))

Saída esperada:

ComponenteValor
price_t_minus_12.288743
price_t3.448862
PnL Real11.601
PnL de Delta7.125
PnL de Gamma0.744
PnL de Vega3.710
PnL de Theta-0.333
PnL de Rho0.0
PnL de Vanna0.343
PnL de Volga0.031
PnL Explicado11.621
Residual-0.020

O residual_pnl captura efeitos de ordem superior e diferenças de modelo entre t-1 e t. Ative cross_greeks=true para incluir contribuições de vanna e volga.


Exemplo 4: Carteira / Greeks em Lote

Para agregação em nível de livro, use o endpoint POST /v1/portfolio/greeks via Power Query ou um pequeno auxiliar em VBA. O endpoint aceita um corpo JSON com múltiplas pernas e retorna Greeks por perna e agregados.

Consulte docs/api.md para o esquema completo de requisição/resposta.


Convenções de Greeks

GreekUnidadeNotas
DeltaabsolutoPor movimento de $1 no spot
GammaabsolutoPor movimento de $1 no spot
Vegapor 1 ponto de volatilidadeou seja, volatilidade decimal × 100
Thetapor dia corridoP&L futuro de um dia corrido passando (reavaliação de 1 dia corrido − preço de hoje). Negativo para uma opção longa em decadência.
Rhopor ponto de taxa de 1%ou seja, taxa decimal × 100
Charmpor dia corrido∂delta/∂t (delta em t − 1/365 − delta hoje). Negativo para uma opção de compra longa — o delta decai em direção ao vencimento

Localização do Log e Registro Estruturado

  • Caminho do log: a variável de ambiente DESKPRICER_LOG_DIR substitui o padrão (C:\ProgramData\DeskPricer\logs no Windows, ~/.local/share/deskpricer/logs em outros sistemas).
  • Formato: Usa o módulo logging da biblioteca padrão do Python com um formatador JSON personalizado e RotatingFileHandler (rotação de 10 MB, 5 backups). Isso substitui a abordagem anterior feita à mão com open().
  • Alterar o caminho:
    $env:DESKPRICER_LOG_DIR = "C:\MyLogs"
    python -m deskpricer.main
    

Solução de Problemas

Falhas na instalação do QuantLib

Se pip install falhar no QuantLib, garanta que você tenha um compilador C++ e CMake, ou use uma wheel pré-compilada. Consulte docs/operator_guide.md para etapas detalhadas.

Surpresas com Zero-DTE

t=0 tem piso de 1 dia corrido para evitar o colapso do QuantLib. Você obterá um pequeno prêmio de valor temporal em vez de valor intrínseco puro. Isso é intencional.

Incompatibilidades de mecanismo/estilo

  • style=european → somente engine=analytic
  • style=american → somente engine=binomial_crr ou binomial_jr

XML vs JSON

O Excel recebe XML por padrão. Para JSON, envie Accept: application/json ou ?format=json.

Códigos de erro

CódigoSignificado
INVALID_INPUTFalha na validação de regra de negócio ou esquema
UNSUPPORTED_COMBINATIONIncompatibilidade de mecanismo/estilo
PRICING_FAILUREErro interno inesperado (nenhum traceback vazado)

Limitações

  • Sem mecanismo FD — BSM de forma fechada para europeias e americanas equivalentes; binomial CRR/JR para outras americanas.
  • QuantLib concorrente via pool de processos — padrão de min(4, cpu_count()) workers (DESKPRICER_WORKERS). As pernas da carteira são processadas em lote em uma única chamada de worker por requisição.
  • Faixas de bump limitadas — bump_spot_rel ≤ 0,1, bump_vol_abs ≤ 0,01, bump_rate_abs ≤ 0,01.
  • Sem banco de dados ou persistência — todo o estado é em memória por requisição.
  • Sem autenticação, TLS ou limitação de taxa — somente local por design.

Executando Testes

pytest tests -v

Decisões de Design

Somente local por design

DeskPricer é construído como uma ferramenta pessoal de desktop, não uma API pública. Isso explica todas as omissões intencionais:

  • Sem autenticação / autorização — somente 127.0.0.1 pode alcançar o serviço.
  • Sem TLS / HTTPS — o tráfego de loopback local não é criptografado por design.
  • Sem limitação de taxa — sem throttling; requisições concorrentes são tratadas por um pool de processos em vez de serializadas em um único estado global do QuantLib.
  • Sem Swagger / Redoc — os documentos OpenAPI ficam ocultos em builds de produção para reduzir a superfície de ataque.
  • XML por padrão — a função WEBSERVICE do Excel não envia Accept: application/json.

Se você precisar de qualquer um desses recursos, DeskPricer é a ferramenta errada. Use um gateway de API adequado ou uma plataforma de precificação completa.

Por que o QuantLib roda em um pool de processos

Os bindings Python do QuantLib dependem de um único objeto Settings.instance() global do processo. Em vez de serializar todas as requisições com um asyncio.Lock, o DeskPricer despacha o trabalho do QuantLib para um ProcessPoolExecutor. Cada processo worker tem suas próprias configurações isoladas, então chamadas concorrentes de agentes ou Excel podem precificar em paralelo sem corromper as datas de avaliação. O tamanho do pool assume o padrão de min(4, cpu_count()) e é configurável via DESKPRICER_WORKERS.

Europeias e americanas economicamente equivalentes ignoram o QuantLib completamente e usam uma implementação BSM em Python puro (bsm_fast), validada contra o QuantLib até seis casas decimais.

Por que a atribuição de PnL usa GET com muitos parâmetros de consulta

Excel's WEBSERVICE function only supports HTTP GET. Since the primary user of this service is Excel, the GET /v1/pnl_attribution endpoint is designed specifically for WEBSERVICE compatibility. Programmatic clients that need a cleaner JSON body can use POST /v1/portfolio/greeks today; a POST alternative for PnL attribution may be added in a future release.


Estrutura do Projeto

DeskPricer/
├── pyproject.toml
├── README.md
├── src/deskpricer/          # FastAPI app + pricing core
│   ├── app.py               # Thin composition root
│   ├── routers/             # APIRouter modules
│   ├── services/            # Pricing orchestration + QL lock
│   ├── pricing/             # QuantLib pricing engines
│   ├── schemas.py           # Pydantic models
│   ├── responses.py         # XML/JSON serializers
│   ├── errors.py            # Custom exceptions
│   ├── logging_config.py    # Structured JSON logging
│   └── main.py              # Uvicorn entrypoint
├── tests/                   # pytest + hypothesis
├── tests/fixtures/          # Regression baseline JSONs
├── scripts/                 # Build + fixture generation
├── sample/                  # Demo Excel workbook
└── docs/                    # API ref + operator guide

Licença

MIT