Execute o Python Windows x64 no Wine, carregue a ProfitDLL no Ubuntu e prepare uma assinatura de trades do WINFUT sem máquina virtual ou bridge externa.
Desenvolver no Linux e precisar consumir uma DLL nativa do Windows costuma levar a três caminhos: montar uma máquina virtual, criar um serviço intermediário ou manter um computador dedicado só para a integração.
Para a ProfitDLL, existe um caminho mais direto: executar o próprio Python para Windows dentro do Wine.
A lógica é simples. O Python nativo do Ubuntu não abre a DLL. O Wine executa um python.exe Windows x64, e esse processo carrega a versão Win64 da ProfitDLL exatamente como faria em uma máquina Windows:
Ubuntu x86_64
└── Wine e prefixo isolado
└── CPython Windows x64
└── ProfitDLL Win64 e dependências
└── infraestrutura Nelogica
callback nativo → TranslateTrade → cópia dos dados → fila → aplicação
Essa abordagem elimina a necessidade de uma bridge externa para um consumidor simples: a aplicação, a DLL e os callbacks ficam no mesmo processo Windows criado pelo Wine.
Dessa forma, este tutorial cobre a preparação do ambiente, a conexão em modo somente Market Data e a assinatura do papel WINFUT/F. Não há envio de ordens.
Essa abordagem elimina a necessidade de uma bridge externa para um consumidor simples: a aplicação, a DLL e os callbacks ficam no mesmo processo Windows criado pelo Wine.
Dessa forma, este tutorial cobre a preparação do ambiente, a conexão em modo somente Market Data e a assinatura do papel WINFUT/F. Não há envio de ordens.
Assuntos que você irá encontrar neste artigo:
- Por que a ProfitDLL no Linux depende do Python para Windows?
- Requisitos e versões usadas neste tutorial;
- Passo a passo: como instalar e configurar a ProfitDLL no Linux;
- O detalhe mais importante está no callback;
- Afinal, é plug and play?
- Como diagnosticar quando não funciona?
- FAQ: Perguntas frequentes sobre a ProfitDLL no Linux.
Por que a ProfitDLL no Linux depende do Python para Windows?
A ProfitDLL é uma biblioteca Win32/Win64 com convenção de chamada stdcall. Ela espera tipos e regras da ABI do Windows, entre eles:
- ponteiros de 64 bits na versão Win64;
- wchar_tde 2 bytes;
- carregamento comctypes.WinDLL;
- callbacksdeclarados com ctypes.WINFUNCTYPE.
O Python nativo do Linux usa outra ABI, carrega bibliotecas ELF e normalmente representa wchar_t com 4 bytes. Apontar o ctypes.CDLL do Linux para ProfitDLL.dll não resolve essas diferenças.
O Wine resolve o problema em outro nível: ele executa o binário do Python para Windows e fornece as APIs Win32 usadas por esse processo.
Por isso, o comando final tem este formato:
wine C:/Python313/python.exe C:/ProfitDLL/app.py
No Wine 11, o executável separado wine64 foi removido. O comando único wine passa a selecionar a arquitetura do binário executado, conforme detalhado nas notas oficiais do Wine 11.
Requisitos e versões usadas neste tutorial
Para manter o tutorial reproduzível, esta é a matriz de referência usada neste artigo, em vez de recomendar apenas “a versão mais recente”:
- Ubuntu 24.04 LTS Desktop x86_64;
- WineHQStable 11.0;
- prefixoWineexclusivo para a ProfitDLL;
- CPython3.13.15 Windows x64, instalador completo;
- conteúdo completo deDLLs/Win64 do pacote oficial daProfitDLL.
O binding deste artigo foi conferido contra a ProfitDLL 4.0.0.41 em 24/08/2026.
Se o pacote oficial já estiver em outra versão quando você ler este texto, registre o novo hash e confira o changelog e o header incluído no SDK.
Também é necessário ter uma licença ativa da ProfitDLL, a chave de ativação e as credenciais do login Nelogica.
Garanta que a mesma licença não esteja conectada por outra instância da DLL, o Profit aberto não conflita com ela; o conflito relevante é outra conexão da própria DLL usando a mesma licença.
Passo a passo: como instalar e configurar a ProfitDLL no Linux
Instale o WineHQ
No Ubuntu 24.04, adicione a arquitetura i386 e o repositório Noble do WineHQ. Confira a receita vigente na documentação oficial para Debian e Ubuntu antes de executar, porque a forma de empacotamento pode mudar.
source /etc/os-release
printf 'ID=%s VERSION_ID=%s CODENAME=%s ARCH=%s\n' \
"$ID" "$VERSION_ID" "$VERSION_CODENAME" "$(dpkg --print-architecture)"
sudo apt update
sudo apt install ca-certificates unzip wget
sudo dpkg --add-architecture i386
sudo mkdir -pm755 /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/winehq-archive.key \
https://dl.winehq.org/wine-builds/winehq.key
sudo wget -O /etc/apt/sources.list.d/winehq-noble.sources \
https://dl.winehq.org/wine-builds/ubuntu/dists/noble/winehq-noble.sources
sudo apt update
apt-cache policy winehq-stable
sudo apt install --install-recommends winehq-stable
wine --version
O primeiro bloco deve mostrar Ubuntu 24.04, codename noble e arquitetura amd64.
Na data de referência deste artigo, o candidato de winehq-stable era o Wine 11.0. Como esse pacote é atualizado com frequência, confira o resultado de apt-cache policy antes de continuar.
Não use sudo apt install wine como substituto: esse comando pode instalar a versão mantida pela distribuição, e não a versão estável do WineHQ usada neste artigo.
Crie um prefixo exclusivo
Um prefixo é o diretório no qual o Wine mantém o C:, o registro e as configurações daquele ambiente Windows. Não misture a ProfitDLL com jogos ou outros aplicativos já existentes em ~/.wine.
PROFITDLL_WINEPREFIX="$HOME/.local/share/wineprefixes/profitdll"
install -d -m 700 "$PROFITDLL_WINEPREFIX"
env WINEPREFIX="$PROFITDLL_WINEPREFIX" \
WINEARCH=win64 \
wineboot --init
env WINEPREFIX="$PROFITDLL_WINEPREFIX" winecfg -v win10
env WINEPREFIX="$PROFITDLL_WINEPREFIX" wineserver --wait
head -n 4 "$PROFITDLL_WINEPREFIX/system.reg"
O início de system.reg deve indicar #arch=win64. Execute o Wine com o usuário normal, nunca como root.
Defina WINEARCH=win64 apenas na criação. Nos próximos comandos, basta reutilizar o mesmo diretório em WINEPREFIX.
Instale o Python Windows x64
Baixe o instalador completo de 64 bits do Python 3.13.15 e confira o SHA-256 publicado pela Python Software Foundation:
wget -O python-3.13.15-amd64.exe \
https://www.python.org/ftp/python/3.13.15/python-3.13.15-amd64.exe
echo "edec09c4853aeae9ac36efb8c9f95b6b8e2fee65eee56d9767a8b7c69c574403 python-3.13.15-amd64.exe" \
| sha256sum -c -
Instale-o num caminho fixo. Como o artigo sempre chama o executável pelo caminho completo, não dependemos do PATH do Wine:
PROFITDLL_WINEPREFIX="$HOME/.local/share/wineprefixes/profitdll"
env WINEPREFIX="$PROFITDLL_WINEPREFIX" \
wine ./python-3.13.15-amd64.exe /quiet \
InstallAllUsers=0 \
'TargetDir=C:\Python313' \
Include_pip=1 \
Include_test=0 \
Include_freethreaded=0 \
Include_launcher=0 \
AssociateFiles=0 \
PrependPath=0 \
Shortcuts=0 \
/log 'C:\python-install.log'
env WINEPREFIX="$PROFITDLL_WINEPREFIX" wineserver --wait
env WINEPREFIX="$PROFITDLL_WINEPREFIX" \
wine 'C:\Python313\python.exe' -VV
Agora valide a ABI que será vista pela ProfitDLL:
env WINEPREFIX="$PROFITDLL_WINEPREFIX" \
wine 'C:\Python313\python.exe' -c \
'import ctypes,struct,sys; assert sys.platform=="win32"; assert struct.calcsize("P")==8; assert ctypes.sizeof(ctypes.c_wchar)==2; assert hasattr(ctypes,"WinDLL"); assert hasattr(ctypes,"WINFUNCTYPE"); print("ABI Windows x64 OK")'
O resultado esperado é ABI Windows x64 OK. Se uma das asserções falhar, pare aqui: esse não é o processo correto para carregar a DLL Win64.
Copie o runtime completo da ProfitDLL
Baixe o pacote oficial da ProfitDLL, registre o hash recebido e extraia-o:
wget -O ProfitDLL.zip \
https://download-setup.nelogica.com.br/connector/latest/ProfitDLL.zip
sha256sum ProfitDLL.zip
unzip ProfitDLL.zip -d ProfitDLL-sdk
Copie todo o conteúdo de DLLs/Win64, preservando arquivos .dat, DLLs auxiliares e subdiretórios:
PROFITDLL_WINEPREFIX="$HOME/.local/share/wineprefixes/profitdll"
PROFITDLL_RUNTIME="$PROFITDLL_WINEPREFIX/drive_c/ProfitDLL"
install -d -m 700 "$PROFITDLL_RUNTIME"
cp -a ProfitDLL-sdk/DLLs/Win64/. \
"$PROFITDLL_RUNTIME/"
test -f "$PROFITDLL_RUNTIME/ProfitDLL.dll"
Não copie apenas o arquivo ProfitDLL.dll e não substitua manualmente as bibliotecas OpenSSL do pacote. A DLL principal pode carregar dependências pelo diretório e consultar arquivos de dados pelo diretório de trabalho.
Crie o consumidor de trades
Salve o código abaixo como:
$PROFITDLL_RUNTIME/app.py
O exemplo usa apenas a biblioteca padrão do Python. Ele valida a ABI, mantém os callbacks vivos, aguarda os três estados de conexão, traduz o trade enquanto o ponteiro ainda é válido e envia apenas uma cópia dos dados para a fila.
from __future__ import annotations import ctypes as C import os import queue import sys import threading from dataclasses import dataclass from getpass import getpass from pathlib import Path NL_OK = 0 LOGIN = 0 MARKET = 2 ACTIVATION = 3 LOGIN_CONNECTED = 0 MARKET_CONNECTED = 4 ACTIVATION_VALID = 0 TICKER = "WINFUT" EXCHANGE = "F" class SystemTime(C.Structure): _fields_ = [ ("wYear", C.c_ushort), ("wMonth", C.c_ushort), ("wDayOfWeek", C.c_ushort), ("wDay", C.c_ushort), ("wHour", C.c_ushort), ("wMinute", C.c_ushort), ("wSecond", C.c_ushort), ("wMilliseconds", C.c_ushort), ] class AssetIdentifier(C.Structure): # Ticker e Exchange são ponteiros válidos somente durante o callback. _fields_ = [ ("Version", C.c_ubyte), ("Ticker", C.c_void_p), ("Exchange", C.c_void_p), ("FeedType", C.c_ubyte), ] class ConnectorTrade(C.Structure): _fields_ = [ ("Version", C.c_ubyte), ("TradeDate", SystemTime), ("TradeNumber", C.c_uint), ("Price", C.c_double), ("Quantity", C.c_longlong), ("Volume", C.c_double), ("BuyAgent", C.c_int), ("SellAgent", C.c_int), ("TradeType", C.c_ubyte), ] CALLBACK = getattr(C, "WINFUNCTYPE", C.CFUNCTYPE) StateCallback = CALLBACK(None, C.c_int, C.c_int) TradeCallback = CALLBACK(None, AssetIdentifier, C.c_size_t, C.c_uint) @dataclass(frozen=True) class TradeEvent: ticker: str exchange: str date: str trade_number: int price: float quantity: int volume: float buy_agent: int sell_agent: int trade_type: int is_edit: bool def return_code(value: int) -> str: unsigned = C.c_uint32(value).value return f"{value} (0x{unsigned:08X})" def preflight() -> None: if sys.platform != "win32" or not hasattr(C, "WinDLL"): raise RuntimeError( "Execute este arquivo com o python.exe Windows dentro do Wine, " "não com o Python nativo do Linux." ) if len(sys.argv) != 1: raise RuntimeError("Execute o exemplo sem argumentos adicionais.") expected = { "ponteiro": (C.sizeof(C.c_void_p), 8), "wchar": (C.sizeof(C.c_wchar), 2), "SystemTime": (C.sizeof(SystemTime), 16), "AssetIdentifier": (C.sizeof(AssetIdentifier), 32), "ConnectorTrade": (C.sizeof(ConnectorTrade), 64), } invalid = { name: (actual, wanted) for name, (actual, wanted) in expected.items() if actual != wanted } if invalid: raise RuntimeError(f"ABI incompatível: {invalid}") expected_offsets = { "AssetIdentifier.Ticker": (AssetIdentifier.Ticker.offset, 8), "AssetIdentifier.Exchange": (AssetIdentifier.Exchange.offset, 16), "ConnectorTrade.TradeDate": (ConnectorTrade.TradeDate.offset, 2), "ConnectorTrade.TradeNumber": (ConnectorTrade.TradeNumber.offset, 20), "ConnectorTrade.Price": (ConnectorTrade.Price.offset, 24), "ConnectorTrade.Quantity": (ConnectorTrade.Quantity.offset, 32), "ConnectorTrade.TradeType": (ConnectorTrade.TradeType.offset, 56), } invalid_offsets = { name: (actual, wanted) for name, (actual, wanted) in expected_offsets.items() if actual != wanted } if invalid_offsets: raise RuntimeError(f"Offsets incompatíveis: {invalid_offsets}") class ProfitFeed: def __init__(self, runtime_dir: Path) -> None: preflight() self.runtime_dir = runtime_dir.resolve() dll_path = self.runtime_dir / "ProfitDLL.dll" if not dll_path.is_file(): raise FileNotFoundError(f"DLL não encontrada: {dll_path}") # Preserva duas formas de descoberta: dependências pelo diretório de # DLLs e arquivos .dat pelo diretório de trabalho. os.chdir(self.runtime_dir) self._dll_directory = os.add_dll_directory(str(self.runtime_dir)) self.dll = C.WinDLL(str(dll_path)) self.events: queue.Queue[TradeEvent] = queue.Queue(maxsize=10_000) self.dropped = 0 self.translate_errors = 0 self.callback_errors = 0 self._metrics_lock = threading.Lock() self._states: dict[int, int] = {} self._state_changed = threading.Condition() self._callback_gate = threading.Lock() self._accept_trades = threading.Event() self._initialized = False self._subscribed = False # Atributos mantêm as referências vivas até DLLFinalize. self._state_callback = StateCallback(self._on_state) self._trade_callback = TradeCallback(self._on_trade) self._configure_signatures() self._check_exports() def _configure_signatures(self) -> None: optional_callback = C.c_void_p self.dll.DLLInitializeMarketLogin.argtypes = [ C.c_wchar_p, C.c_wchar_p, C.c_wchar_p, StateCallback, optional_callback, optional_callback, optional_callback, optional_callback, optional_callback, optional_callback, optional_callback, ] self.dll.DLLInitializeMarketLogin.restype = C.c_int self.dll.SetTradeCallbackV2.argtypes = [TradeCallback] self.dll.SetTradeCallbackV2.restype = C.c_int self.dll.TranslateTrade.argtypes = [ C.c_size_t, C.POINTER(ConnectorTrade), ] self.dll.TranslateTrade.restype = C.c_int self.dll.SubscribeTicker.argtypes = [C.c_wchar_p, C.c_wchar_p] self.dll.SubscribeTicker.restype = C.c_int self.dll.UnsubscribeTicker.argtypes = [C.c_wchar_p, C.c_wchar_p] self.dll.UnsubscribeTicker.restype = C.c_int self.dll.DLLFinalize.argtypes = [] self.dll.DLLFinalize.restype = C.c_int def _check_exports(self) -> None: required = ( "DLLInitializeMarketLogin", "SetTradeCallbackV2", "TranslateTrade", "SubscribeTicker", "UnsubscribeTicker", "DLLFinalize", ) missing = [name for name in required if not hasattr(self.dll, name)] if missing: raise RuntimeError(f"Exports ausentes na DLL: {missing}") def _on_state(self, state_type: int, result: int) -> None: # Callback nativo: atualiza memória e retorna, sem print ou I/O. try: with self._state_changed: self._states[int(state_type)] = int(result) self._state_changed.notify_all() except BaseException: with self._metrics_lock: self.callback_errors += 1 def _on_trade( self, asset: AssetIdentifier, trade_pointer: int, flags: int, ) -> None: self._callback_gate.acquire() try: if not self._accept_trades.is_set(): return trade = ConnectorTrade(Version=0) result = self.dll.TranslateTrade(trade_pointer, C.byref(trade)) if result != NL_OK: with self._metrics_lock: self.translate_errors += 1 return # Copie tudo antes de retornar. Nenhum ponteiro nativo vai à fila. ticker = ( C.cast(asset.Ticker, C.c_wchar_p).value if asset.Ticker else "" ) exchange = ( C.cast(asset.Exchange, C.c_wchar_p).value if asset.Exchange else "" ) timestamp = ( f"{trade.TradeDate.wDay:02d}/" f"{trade.TradeDate.wMonth:02d}/" f"{trade.TradeDate.wYear:04d} " f"{trade.TradeDate.wHour:02d}:" f"{trade.TradeDate.wMinute:02d}:" f"{trade.TradeDate.wSecond:02d}." f"{trade.TradeDate.wMilliseconds:03d}" ) event = TradeEvent( ticker=ticker, exchange=exchange, date=timestamp, trade_number=int(trade.TradeNumber), price=float(trade.Price), quantity=int(trade.Quantity), volume=float(trade.Volume), buy_agent=int(trade.BuyAgent), sell_agent=int(trade.SellAgent), trade_type=int(trade.TradeType), is_edit=bool(flags & 1), ) try: self.events.put_nowait(event) except queue.Full: with self._metrics_lock: self.dropped += 1 except BaseException: # Exceções não podem escapar de um callback ctypes. with self._metrics_lock: self.callback_errors += 1 finally: self._callback_gate.release() def initialize(self, key: str, user: str, password: str) -> None: result = self.dll.DLLInitializeMarketLogin( key, user, password, self._state_callback, None, None, None, None, None, None, None, ) if result != NL_OK: raise RuntimeError( "DLLInitializeMarketLogin falhou: " + return_code(result) ) self._initialized = True result = self.dll.SetTradeCallbackV2(self._trade_callback) if result != NL_OK: raise RuntimeError( "SetTradeCallbackV2 falhou: " + return_code(result) ) def wait_until_ready(self, timeout: float = 30.0) -> dict[int, int]: def ready() -> bool: return ( self._states.get(LOGIN) == LOGIN_CONNECTED and self._states.get(MARKET) == MARKET_CONNECTED and self._states.get(ACTIVATION) == ACTIVATION_VALID ) with self._state_changed: if not self._state_changed.wait_for(ready, timeout=timeout): raise TimeoutError( f"Conexão incompleta após {timeout:.0f}s: {self._states}" ) return dict(self._states) def subscribe(self, ticker: str, exchange: str) -> None: # Habilite antes da chamada para não perder um evento que possa chegar # imediatamente depois de a assinatura ser aceita. self._accept_trades.set() result = self.dll.SubscribeTicker(ticker, exchange) if result != NL_OK: self._accept_trades.clear() raise RuntimeError( "SubscribeTicker falhou: " + return_code(result) ) self._subscribed = True def close(self) -> tuple[int | None, int | None]: unsubscribe_result = None finalize_result = None # Impede novas traduções e espera qualquer TranslateTrade já iniciado. self._accept_trades.clear() with self._callback_gate: pass if self._subscribed: unsubscribe_result = self.dll.UnsubscribeTicker(TICKER, EXCHANGE) self._subscribed = False if self._initialized: finalize_result = self.dll.DLLFinalize() self._initialized = False self._dll_directory.close() return unsubscribe_result, finalize_result def main() -> None: preflight() print("ABI Windows x64: OK") feed = ProfitFeed(Path(__file__).resolve().parent) try: key = getpass("Chave de ativação: ").strip() user = input("Usuário Nelogica: ").strip() password = getpass("Senha: ") if not key or not user or not password: raise RuntimeError("Chave, usuário e senha são obrigatórios.") feed.initialize(key, user, password) print("Inicialização aceita. Aguardando os estados assíncronos...") states = feed.wait_until_ready(timeout=30) print(f"Conexão pronta: {states}") feed.subscribe(TICKER, EXCHANGE) print(f"Assinatura {TICKER}/{EXCHANGE}: OK. Ctrl+C para sair.") reported_drops = 0 while True: try: event = feed.events.get(timeout=1.0) except queue.Empty: continue print( f"{event.date} {event.ticker}/{event.exchange} " f"{event.price:.2f} x {event.quantity} " f"trade={event.trade_number} tipo={event.trade_type} " f"edit={event.is_edit}" ) if feed.dropped != reported_drops: reported_drops = feed.dropped print(f"AVISO: fila cheia, descartados={reported_drops}") except KeyboardInterrupt: print("\nEncerrando...") finally: unsubscribe, finalize = feed.close() print( "Resumo: " f"drops={feed.dropped}, " f"translate_errors={feed.translate_errors}, " f"callback_errors={feed.callback_errors}" ) if unsubscribe is not None: print("UnsubscribeTicker:", return_code(unsubscribe)) if finalize is not None: print("DLLFinalize:", return_code(finalize)) if __name__ == "__main__": main()
Execute
Use o mesmo prefixo criado anteriormente e chame o Python pelo caminho exato:
PROFITDLL_WINEPREFIX="$HOME/.local/share/wineprefixes/profitdll"
PROFITDLL_RUNTIME="$PROFITDLL_WINEPREFIX/drive_c/ProfitDLL"
cd "$PROFITDLL_RUNTIME"
env WINEPREFIX="$PROFITDLL_WINEPREFIX" \
wine 'C:\Python313\python.exe' 'C:\ProfitDLL\app.py'
As credenciais são solicitadas de forma interativa. Elas não ficam registradas no código nem nos argumentos da linha de comando.
Execute o arquivo sem argumentos adicionais e, no primeiro teste, evite IDEs ou wrappers que alterem a linha de comando do processo.
A ProfitDLL pode interpretar argumentos extras como configuração manual de servidor, o próprio preflight interrompe o exemplo caso encontre algum.
O primeiro nível de sucesso independe de haver negócios naquele instante:
ABI Windows x64: OK
Inicialização aceita. Aguardando os estados assíncronos...
Conexão pronta: {0: 0, 2: 4, 3: 0}
Assinatura WINFUT/F: OK. Ctrl+C para sair.
Durante o pregão, cada negócio traduzido passa a aparecer no terminal. Os valores abaixo são apenas um exemplo do formato:
24/08/2026 10:15:31.207 WINFUT/F 141250.00 x 1 trade=874512 tipo=2 edit=False
24/08/2026 10:15:31.211 WINFUT/F 141245.00 x 2 trade=874513 tipo=3 edit=False
Receber SubscribeTicker: 0 não garante que um trade chegará imediatamente. Fora do pregão ou num intervalo sem negócios, a assinatura pode estar correta e a fila continuar vazia.
O detalhe mais importante está no callback
O callback recebe um ponteiro opaco para o trade. Esse ponteiro pertence à DLL e sua validade termina quando o callback retorna.
Por isso, o exemplo segue esta ordem:
- chamaTranslateTradeainda dentro do callback;
- trata o retorno 0 como sucesso;
- copiastrings, números e timestamp para um TradeEvent em Python;
- usaput_nowaitem uma fila limitada;
- retorna imediatamente.
O terminal é atualizado pela thread principal. Não há print, gravação em banco, requisição de rede ou processamento de indicador dentro da ConnectorThread.
As referências de StateCallback e TradeCallback também permanecem guardadas no objeto ProfitFeed.
Se o garbage collector liberar uma delas, a DLL pode tentar chamar um endereço inválido e encerrar o processo sem gerar uma exceção Python útil.
Afinal, é plug and play?
Depois que o prefixo, o Python Windows e a pasta Win64 estão corretos, o código da aplicação é praticamente o mesmo que seria usado em um Windows x64.
Essa é a parte simples do Wine: não há protocolo intermediário nem serialização entre dois serviços.
O Wine mantém a ProfitDLL como uma biblioteca Windows e fornece as APIs Win32 usadas pelo processo. As regras de autenticação, callbacks, encerramento, reconexão e controle da fila continuam iguais às da aplicação executada diretamente no Windows.
O pipeline mínimo fica assim:
Wine → Python Windows x64 → WinDLL → conexão assíncrona
→ gate Login/Market/Activation → SubscribeTicker
→ TranslateTrade → fila → sua aplicação
Com esse stream funcionando, o restante da aplicação segue em terreno conhecido do Python: persistir os trades, montar candles, calcular indicadores ou alimentar um dashboard, sempre fora do callback nativo.
Como diagnosticar quando não funciona?
Erro “ctypes.WinDLL não existe”: o que fazer?
O arquivo foi executado pelo Python nativo do Ubuntu. Use explicitamente:
wine C:/Python313/python.exe C:/ProfitDLL/app.py
A validação da ABI falhou: como resolver?
Confirme que o instalador é x64, que o prefixo é Win64 e que o comando não encontrou outro python.exe no PATH.
A DLL não carrega ou aparece erro de dependência: como resolver?
Verifique se todo o conteúdo de DLLs/Win64 foi copiado, incluindo subdiretórios e arquivos .dat. Mantenha C:\ProfitDLL como diretório de trabalho e não substitua as DLLs OpenSSL do pacote por conta própria.
Se ainda faltar uma dependência, gere um log de carregamento sem ocultar os avisos do Wine:
PROFITDLL_WINEPREFIX="$HOME/.local/share/wineprefixes/profitdll"
env WINEPREFIX="$PROFITDLL_WINEPREFIX" \
WINEDEBUG='-all,err+all,warn+all,trace+loaddll' \
wine 'C:\Python313\python.exe' 'C:\ProfitDLL\app.py' \
2> wine-load.log
Inicialização retornou zero, mas a conexão não ficou pronta: por que?
O retorno zero da inicialização apenas aceita o pedido. Consulte o dicionário mostrado no timeout:
- tipo 0: login, sucesso em 0;
- tipo 2: Market Data, sucesso em 4;
- tipo 3: ativação, sucesso em 0.
Login 200 é um erro genérico. O primeiro diagnóstico é verificar se a mesma licença da DLL está conectada em outro processo ou computador. Evite tentativas repetidas em sequência rápida, pois erros sucessivos podem bloquear a senha.
Se DLLInitializeMarketLogin permanecer travado em determinadas licenças, valide com o suporte se o seu acesso exige o fluxo de DLLInitializeLogin. Não altere a assinatura da função sem ajustar todos os callbacks correspondentes.
A assinatura retornou zero, mas não há trades: o que verificar?
Confirme que os três estados chegaram ao resultado esperado, que o mercado está em pregão e que o par é exatamente WINFUT/F. F é a exchange de futuros BM&F; usar uma constante numérica ou outra letra produz um resultado diferente.
O processo trava depois de alguns eventos: o que revisar?
Revise primeiro:
- WinDLLe WINFUNCTYPE, não CDLL e CFUNCTYPE, no processo Windows;
- tamanhos das estruturas;
- referências doscallbacksmantidas vivas;
- TranslateTrade== 0 tratado como sucesso;
- nenhum ponteiro nativo guardado após ocallback;
-
nenhum trabalho bloqueante dentro docallback.
A próxima execução não conecta: o que fazer?
Espere o processo anterior terminar e o DLLFinalize retornar. A ProfitDLL não deve ser finalizada e inicializada novamente dentro do mesmo processo. Uma nova conexão deve começar em um novo processo Python.
Depois de validar a conexão via Wine, o próximo passo natural é ter uma fonte de dados histórica e em tempo real robusta para alimentar sua aplicação.
O Data Solution da Nelogica reúne mais de 30 anos de dados da B3 e integra diretamente com a ProfitDLL, sem custo adicional de infraestrutura para consulta de histórico.
Conheça a ProfitDLL: clique aqui.
FAQ: Perguntas frequentes sobre a ProfitDLL no Linux
- A ProfitDLL tem uma versão nativa para Linux?
Não. A ProfitDLL é uma biblioteca Win32/Win64 com convenção de chamada stdcall, feita para a ABI do Windows. No Linux, ela é executada através do Wine, que roda o Python para Windows e fornece as APIs Win32 necessárias para carregar a DLL.
- É possível usar o Python nativo do Ubuntu para carregar a ProfitDLL?
Não diretamente. O Python nativo do Linux usa outra ABI, carrega bibliotecas ELF e representa wchar_t com 4 bytes, enquanto a ProfitDLL espera wchar_t de 2 bytes e carregamento via ctypes.WinDLL. Apontar o ctypes.CDLL do Linux para o arquivo ProfitDLL.dll não resolve essas diferenças. É necessário executar o Python para Windows dentro do Wine.
- É preciso usar máquina virtual para rodar a ProfitDLL no Linux?
Não. Com o Wine, o Python para Windows, a DLL e os callbacks rodam no mesmo processo Windows criado pelo Wine, sem necessidade de máquina virtual ou de uma bridge externa para um consumidor simples.
- Qual versão do Wine e do Python foi usada neste tutorial?
Este tutorial usa Ubuntu 24.04 LTS Desktop x86_64, WineHQ Stable 11.0 e CPython 3.13.15 Windows x64 (instalador completo), com o binding conferido contra a ProfitDLL 4.0.0.41 em 24/08/2026. Se a versão oficial da ProfitDLL já tiver mudado quando você ler este conteúdo, confira o changelog e o header incluído no SDK.
- Por que o ponteiro do callback não pode ser guardado para uso posterior?
O callback da ProfitDLL recebe um ponteiro opaco para o trade, que pertence à DLL e perde a validade assim que o callback retorna. Por isso, o exemplo chama TranslateTrade ainda dentro do callback e copia os dados (strings, números e timestamp) para um objeto Python antes de retornar.
- O que fazer se a inicialização retornar zero, mas a conexão não ficar pronta?
Um retorno zero na inicialização indica apenas que o pedido foi aceito, não que a conexão está pronta. É necessário aguardar os três estados assíncronos: login (sucesso em 0), Market Data (sucesso em 4) e ativação (sucesso em 0). Se a licença já estiver conectada em outro processo ou computador, a conexão não avança.
Para mais informações, encaminhe um e-mail para corporativo@nelogica.com.br.
Achou útil este conteúdo?
Não esqueça de nos avaliar abaixo.
Desejamos bons trades!