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:
| Planilha | O que mostra |
|---|---|
| Greeks | Opção de Compra Europeia de Bitcoin — spot de $75K, strike de $100K, vencimento de 3M, volatilidade de 50% |
| ImpliedVol | Recupera ~68,3% de volatilidade implícita a partir de um preço de mercado de $3.398,71 |
| PnL Attribution | Decompõ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+FILTERXMLfuncionam imediatamente; JSON disponível viaAccept: application/json - Somente localhost — vincula-se a
127.0.0.1; sem exposição à rede
Convenções
| Greek | Unidade / Convenção |
|---|---|
delta | Absoluto (∂V/∂S) |
gamma | Absoluto (∂²V/∂S²) |
vega | Por ponto de volatilidade de 1% |
theta | Por 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. |
rho | Por ponto de taxa de 1% (somente taxa livre de risco; sem rho de rendimento de dividendos ou custo de empréstimo) |
charm | Por 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élula | Fó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:
| Coluna | Rótulo | Valor de Exemplo |
|---|---|---|
| C | Spot | 100 |
| K | Strike | 105 |
| T | Tempo até o vencimento (anos) | 0.25 |
| R | Taxa livre de risco | 0.05 |
| Q | Rendimento de dividendos | 0.02 |
| B | Custo de empréstimo (opcional, padrão 0,0) | 0.0 |
| V | Volatilidade | 0.20 |
| TYPE | Tipo de opção | call |
| STYLE | Estilo | european |
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ída | Fó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:
| Greek | Valor |
|---|---|
| Preço | 2.288743 |
| Delta | 0.356244 |
| Gamma | 0.037206 |
| Vega | 0.185519 |
| Theta | -0.033315 |
| Rho | 0.083111 |
| Charm | -0.001241 |
Dica: Envolva cada
FILTERXMLemIFERROR(...,"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:
| Campo | Valor em t-1 | Valor em t |
|---|---|---|
| Spot | 100 | 102 |
| Tempo | 0.25 | 0.2466 |
| Vol | 0.20 | 0.22 |
| Taxa | 0.05 | 0.05 |
| Div | 0.02 | 0.02 |
| Empréstimo | 0.0 | 0.0 |
| Qtd | 10 | — |
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:
| Componente | Fó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:
| Componente | Valor |
|---|---|
| price_t_minus_1 | 2.288743 |
| price_t | 3.448862 |
| PnL Real | 11.601 |
| PnL de Delta | 7.125 |
| PnL de Gamma | 0.744 |
| PnL de Vega | 3.710 |
| PnL de Theta | -0.333 |
| PnL de Rho | 0.0 |
| PnL de Vanna | 0.343 |
| PnL de Volga | 0.031 |
| PnL Explicado | 11.621 |
| Residual | -0.020 |
O
residual_pnlcaptura efeitos de ordem superior e diferenças de modelo entre t-1 e t. Ativecross_greeks=truepara 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
| Greek | Unidade | Notas |
|---|---|---|
| Delta | absoluto | Por movimento de $1 no spot |
| Gamma | absoluto | Por movimento de $1 no spot |
| Vega | por 1 ponto de volatilidade | ou seja, volatilidade decimal × 100 |
| Theta | por dia corrido | P&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. |
| Rho | por ponto de taxa de 1% | ou seja, taxa decimal × 100 |
| Charm | por 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_DIRsubstitui o padrão (C:\ProgramData\DeskPricer\logsno Windows,~/.local/share/deskpricer/logsem outros sistemas). - Formato: Usa o módulo
loggingda biblioteca padrão do Python com um formatador JSON personalizado eRotatingFileHandler(rotação de 10 MB, 5 backups). Isso substitui a abordagem anterior feita à mão comopen(). - 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→ somenteengine=analyticstyle=american→ somenteengine=binomial_crroubinomial_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ódigo | Significado |
|---|---|
INVALID_INPUT | Falha na validação de regra de negócio ou esquema |
UNSUPPORTED_COMBINATION | Incompatibilidade de mecanismo/estilo |
PRICING_FAILURE | Erro 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.1pode 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
WEBSERVICEdo Excel não enviaAccept: 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