Usando a biblioteca yfinance no LibreOffice Calc

Usando a biblioteca yfinance no LibreOffice Calc

Já vimos no post anterior que LibreOffice oferece recursos para rodar macros em Python. Um biblioteca de ferramentas denominada ScriptForge busca simplificar o desenvolvimento dessas macros e a extensão APSO (Alternative Python Script Organizer) facilita ainda mais a criação e organização.

A interface de APSO oferece atualmente uma versão beta do Package manager, que se propõe a instalar e desinstalar pacotes pip. Todavia, esse recurso não funciona para todas bibliotecas, entre elas a yfinance. E é sobre isso que falaremos neste artigo, e como contornar essa dificuldade.

Antes de mais nada precisamos instalar a extensão APSO. Veja no post anterior como instalar e configurar o que precisamos na APSO.

Depois de instalada a extensão, vamos analisar as dificuldades encontradas. A primeira delas é que as macros Python no LibreOffice executam em seu próprio ambiente virtual. Se você clicar em Ferramentas -> Macros -> Organizar scripts em Python, em seguida no botão Menu e Python shell, verá que a versão do Python que executa é possivelmente diferente da versão que você tem no seu computador. Isso é um indício de que os ambientes são diferentes.

O ambiente de execução de scripts dentro de softwares de planilha — como o LibreOffice — é limitado, controlado e isolado. Ele foi pensado para automações simples, integração com a própria planilha e pequenas rotinas de apoio. Não foi projetado para rodar bibliotecas modernas de mercado financeiro, que dependem de camadas mais profundas do sistema operacional, extensões nativas e dependências externas.

Bibliotecas como o yfinance, por exemplo, utilizam componentes que exigem:
  • dependências compiladas
  • bibliotecas de baixo nível
  • controle preciso do ambiente Python
Forçar esse tipo de ferramenta a rodar dentro do ambiente interno da planilha gera uma cadeia de problemas: conflitos de versões, erros difíceis de diagnosticar, dependência do sistema do usuário e, principalmente, fragilidade.

A solução adotada neste artigo segue um princípio simples e poderoso: cada ferramenta deve fazer apenas o que ela faz melhor.

A planilha continua responsável por:
  • registrar dados
  • processar informações
  • apresentar visões claras
  • apoiar decisões
Já a coleta de dados externos — como cotações de mercado — acontece em um script Python independente, executado fora da planilha, em um ambiente controlado e previsível.
A comunicação entre esses dois mundos é feita de forma explícita, por meio da execução de um processo externo. A planilha pede a informação. O script busca, processa e devolve o resultado. Não há mágica, nem dependências invisíveis.

Essa separação, como se fossem duas camadas, traz vantagens importantes:
  • isolamento de erros
  • facilidade de manutenção
  • possibilidade de atualizar o script sem mexer na planilha
  • liberdade para usar bibliotecas modernas sem limitações
Resumindo, vamos escrever dois scripts. Um que executa em um ambiente Python no sistema do usuário. E a macro Python que executa no LibreOffice e chama o primeiro script por meio da biblioteca subprocess do Python.

A biblioteca subprocess é usada para gerenciar e interagir com processos externos (programas ou comandos do sistema operacional) diretamente do seu script Python. Ou seja, a macro Python na planilha vai chamar um comando do shell do sistema operacional, tal como Python script_externo.py, por exemplo.

Porém, antes de partirmos para a solução, precisamos falar das variáveis de ambiente PYTHONHOME e PYTHONPATH, usadas pelo interpretador Python para localizar módulos e bibliotecas, mas servem a propósitos distintos e, na maioria das vezes, não precisam ser configuradas manualmente, e geralmente não são mesmo. 

Se você executar uma macro no LibreOffice que exibe essas variáveis, verá que PYTHONPATH retorna caminhos das bibliotecas que a própria ferramenta utiliza para executar seus scripts, ou seja, o LibreOffice seta essas variáveis globalmente. Desse modo, essas variáveis vazam para o script externo quando o executamos via subprocess.

Então vamos à nossa solução.

Primeiro passo

No shell do seu sistema, digite os seguintes comandos para criar uma pasta, um ambiente virtual e instalar as bibliotecas que serão usadas no script externo:

mkdir external_scripts
cd external_scripts
python3 -m venv .venv
source .venv/bin/activate
pip install yfinance

Segundo passo

Crie o script externo denominado price.py, por exemplo. Neste exemplo, o script espera um argumento, o qual deve ser um ticker (código da empresa na bolsa) válido e usa o yfinance para buscar o último preço desse ativo. 

import sys
import yfinance as yf

ticker = sys.argv[1]
price = yf.Ticker(ticker).history(period="1d")["Close"].iloc[-1]
print(price)

Terceiro passo

Crie a macro Python no LibreOffice Calc.

import subprocess
import os


def update_price():
    env = os.environ.copy()

    # REMOVE as variáveis de ambiente do LibreOffice
    env.pop("PYTHONHOME", None)
    env.pop("PYTHONPATH", None)
    
    # Define o comando "python price.py PETR3.SA"
    cmd = [
        "/home/carlos/external_scripts/.venv/bin/python",
        "/home/carlos/external_scripts/price.py",
        "PETR3.SA"
    ]
    
    # Executa o comando no shell e captura o resultado (python price.py PETR3.SA)
    # Aqui é necessário tratar exceções
    result = subprocess.run(cmd, capture_output=True, text=True, env=env)
    price = float(result.stdout.strip())

    # Escreve o preço na célula A1
    doc = XSCRIPTCONTEXT.getDocument()
    sheet = doc.CurrentController.ActiveSheet
    sheet.getCellByPosition(0, 0).Value = price

Observe que removemos as variáveis de ambiente antes de executar o script externo. No list cmd informamos extamente o caminho do comando python, o caminho do script price.py e o argumento.

O subprocess retorna um objeto, do qual nos interessa stdout, que é o que foi exibido na saída padrão. Esse valor é uma string, que é convertido para float e exibido na célula A1. A exibição usa os recursos de macros da LibreOffice, tais como XSCRIPTCONTEXT, uma interface fornecida aos scripts que oferece um meio de acesso às diversas interfaces que eles podem precisar para executar alguma ação em um documento.

Conclusão

Esta é uma solução que roda exclusivamente na máquina local do usuário. Uma outra opção, que depende de outros recursos, é criar uma API REST que tem um endpoint para acesso à yfinance. E a macro usaria requests para fazer uma requisição a essa API.

Comentários