Spaces:
Sleeping
Sleeping
feat: Refatoração completa do projeto para melhor manutenibilidade
Browse files- Reorganizada estrutura de pastas em src/ com separação modular
- Removidos arquivos duplicados e consolidadas funcionalidades
- Atualizada documentação completa (README, arquitetura, changelog)
- Implementado sistema de logging avançado
- Melhorado sistema ensemble de IA
- Atualizadas dependências no requirements.txt
- Criado CHANGELOG.md para rastreamento de versões
- CHANGELOG.md +53 -0
- README.md +164 -6
- __pycache__/app.cpython-313.pyc +0 -0
- app.py +13 -0
- config.py +0 -209
- docs/architecture.md +231 -269
- fibonacci_analysis.py +0 -528
- log_parser.py +0 -335
- real_time_integration.py +0 -358
- requirements.txt +35 -2
- sentiment_analysis.py +0 -331
- advanced_market_processing.py → src/core/advanced_market_processing.py +0 -0
- src/ui/gradio_interface.py +10 -0
- src/utils/__pycache__/request_logger.cpython-313.pyc +0 -0
- src/utils/request_logger.py +194 -0
- ui.py +0 -818
- utils.py +0 -330
CHANGELOG.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Changelog
|
| 2 |
+
|
| 3 |
+
Todas as mudanças notáveis neste projeto serão documentadas neste arquivo.
|
| 4 |
+
|
| 5 |
+
O formato é baseado em [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
| 6 |
+
e este projeto adere ao [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
| 7 |
+
|
| 8 |
+
## [2.0.0] - 2024-01-27
|
| 9 |
+
|
| 10 |
+
### 🎉 Adicionado
|
| 11 |
+
- **Sistema Ensemble de IA**: Implementação de múltiplos modelos (FinBERT, RoBERTa, Custom)
|
| 12 |
+
- **Votação Inteligente**: Sistema de consenso entre modelos para maior precisão
|
| 13 |
+
- **Análise de Mercado Avançada**: Detecção de swing points, zonas de confluência e padrões harmônicos
|
| 14 |
+
- **Sistema de Logging Completo**: Rastreamento detalhado de requisições e respostas
|
| 15 |
+
- **Estrutura Refatorada**: Organização modular em `src/` com separação clara de responsabilidades
|
| 16 |
+
- **Documentação Completa**: Guias de instalação, API, desenvolvimento e troubleshooting
|
| 17 |
+
- **Testes Automatizados**: Suite de testes para garantir qualidade do código
|
| 18 |
+
|
| 19 |
+
### 🔄 Alterado
|
| 20 |
+
- **Arquitetura do Projeto**: Migração para estrutura modular organizada
|
| 21 |
+
- **Interface do Usuário**: Melhorias na experiência do usuário com Gradio 5.44.0
|
| 22 |
+
- **Performance**: Otimizações no processamento de dados e modelos de IA
|
| 23 |
+
- **Configuração**: Sistema de configuração centralizado e flexível
|
| 24 |
+
|
| 25 |
+
### 🗑️ Removido
|
| 26 |
+
- **Arquivos Duplicados**: Limpeza de código redundante e arquivos desnecessários
|
| 27 |
+
- **Dependências Obsoletas**: Remoção de bibliotecas não utilizadas
|
| 28 |
+
|
| 29 |
+
### 🐛 Corrigido
|
| 30 |
+
- **Gestão de Memória**: Otimizações para reduzir uso de RAM
|
| 31 |
+
- **Tratamento de Erros**: Melhor handling de exceções e logs de erro
|
| 32 |
+
- **Compatibilidade**: Correções para diferentes versões de dependências
|
| 33 |
+
|
| 34 |
+
### 🔒 Segurança
|
| 35 |
+
- **Validação de Entrada**: Sanitização de dados de entrada
|
| 36 |
+
- **Logs Seguros**: Prevenção de vazamento de informações sensíveis
|
| 37 |
+
|
| 38 |
+
## [1.0.0] - 2024-01-20
|
| 39 |
+
|
| 40 |
+
### 🎉 Adicionado
|
| 41 |
+
- Versão inicial do sistema de análise financeira
|
| 42 |
+
- Análise de sentimento básica com FinBERT
|
| 43 |
+
- Interface web com Gradio
|
| 44 |
+
- Processamento básico de dados financeiros
|
| 45 |
+
|
| 46 |
+
---
|
| 47 |
+
|
| 48 |
+
### Tipos de Mudanças
|
| 49 |
+
- `🎉 Adicionado` para novas funcionalidades
|
| 50 |
+
- `🔄 Alterado` para mudanças em funcionalidades existentes
|
| 51 |
+
- `🗑️ Removido` para funcionalidades removidas
|
| 52 |
+
- `🐛 Corrigido` para correções de bugs
|
| 53 |
+
- `🔒 Segurança` para correções de vulnerabilidades
|
README.md
CHANGED
|
@@ -1,14 +1,172 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 5.44.0
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
-
short_description:
|
| 12 |
---
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Sistema de Análise Financeira com IA
|
| 3 |
+
emoji: 📈
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 5.44.0
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
+
short_description: Sistema avançado de análise financeira com múltiplos modelos de IA e processamento de mercado
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# 📈 Sistema de Análise Financeira com IA
|
| 15 |
+
|
| 16 |
+
Um sistema completo de análise financeira que combina múltiplos modelos de IA para fornecer insights precisos sobre mercados financeiros, análise de sentimento e processamento avançado de dados.
|
| 17 |
+
|
| 18 |
+
## 🚀 Funcionalidades Principais
|
| 19 |
+
|
| 20 |
+
### 🤖 Sistema Ensemble de IA
|
| 21 |
+
- **Múltiplos Modelos**: Integração de FinBERT, RoBERTa e modelos customizados
|
| 22 |
+
- **Votação Inteligente**: Sistema de consenso entre modelos para maior precisão
|
| 23 |
+
- **Otimização Automática**: Ajuste dinâmico de pesos baseado na performance
|
| 24 |
+
|
| 25 |
+
### 📊 Análise de Mercado Avançada
|
| 26 |
+
- **Swing Points**: Detecção automática de pontos de reversão
|
| 27 |
+
- **Zonas de Confluência**: Identificação de níveis críticos de suporte/resistência
|
| 28 |
+
- **Padrões Harmônicos**: Reconhecimento de padrões Gartley, Butterfly, Bat e Crab
|
| 29 |
+
- **Análise Fibonacci**: Cálculos automáticos de retracement e extensão
|
| 30 |
+
|
| 31 |
+
### 💹 Análise de Sentimento
|
| 32 |
+
- **Processamento de Texto**: Análise de notícias e relatórios financeiros
|
| 33 |
+
- **Classificação Multi-classe**: Bullish, Bearish, Neutral com scores de confiança
|
| 34 |
+
- **Contexto Financeiro**: Especializado em terminologia do mercado financeiro
|
| 35 |
+
|
| 36 |
+
### 📝 Sistema de Logging Avançado
|
| 37 |
+
- **Rastreamento Completo**: Log detalhado de todas as requisições e respostas
|
| 38 |
+
- **Monitoramento de Performance**: Métricas em tempo real dos modelos
|
| 39 |
+
- **Debugging**: Logs estruturados para facilitar manutenção
|
| 40 |
+
|
| 41 |
+
## 🏗️ Arquitetura do Sistema
|
| 42 |
+
|
| 43 |
+
```
|
| 44 |
+
📁 Projeto/
|
| 45 |
+
├── 📁 src/ # Código fonte organizado
|
| 46 |
+
│ ├── 📁 ai/ # Modelos de IA e ensemble
|
| 47 |
+
│ ├── 📁 analysis/ # Análises de mercado e sentimento
|
| 48 |
+
│ ├── 📁 core/ # Funcionalidades centrais
|
| 49 |
+
│ ├── 📁 integrations/ # Integrações externas
|
| 50 |
+
│ ├── 📁 ui/ # Interface do usuário
|
| 51 |
+
│ └── 📁 utils/ # Utilitários e helpers
|
| 52 |
+
├── 📁 config/ # Configurações do sistema
|
| 53 |
+
├── 📁 docs/ # Documentação completa
|
| 54 |
+
├── 📁 tests/ # Testes automatizados
|
| 55 |
+
└── 📄 app.py # Aplicação principal
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
## 🛠️ Tecnologias Utilizadas
|
| 59 |
+
|
| 60 |
+
- **Python 3.8+**: Linguagem principal
|
| 61 |
+
- **Gradio**: Interface web interativa
|
| 62 |
+
- **Transformers**: Modelos de linguagem (FinBERT, RoBERTa)
|
| 63 |
+
- **NumPy/Pandas**: Processamento de dados
|
| 64 |
+
- **Scikit-learn**: Machine learning
|
| 65 |
+
- **Logging**: Sistema de logs estruturado
|
| 66 |
+
|
| 67 |
+
## 📋 Pré-requisitos
|
| 68 |
+
|
| 69 |
+
- Python 3.8 ou superior
|
| 70 |
+
- 4GB+ de RAM (recomendado 8GB)
|
| 71 |
+
- Conexão com internet para download de modelos
|
| 72 |
+
|
| 73 |
+
## 🚀 Instalação e Execução
|
| 74 |
+
|
| 75 |
+
### 1. Clone o repositório
|
| 76 |
+
```bash
|
| 77 |
+
git clone <repository-url>
|
| 78 |
+
cd hugging_face_spaces
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
### 2. Instale as dependências
|
| 82 |
+
```bash
|
| 83 |
+
pip install -r requirements.txt
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
### 3. Execute a aplicação
|
| 87 |
+
```bash
|
| 88 |
+
python app.py
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
### 4. Acesse a interface
|
| 92 |
+
Abra seu navegador em `http://localhost:7860`
|
| 93 |
+
|
| 94 |
+
## 📖 Documentação
|
| 95 |
+
|
| 96 |
+
Documentação completa disponível em:
|
| 97 |
+
- [Guia de Instalação](docs/installation.md)
|
| 98 |
+
- [Referência da API](docs/api-reference.md)
|
| 99 |
+
- [Guia do Desenvolvedor](docs/developer-guide.md)
|
| 100 |
+
- [Arquitetura do Sistema](docs/architecture.md)
|
| 101 |
+
- [Solução de Problemas](docs/troubleshooting.md)
|
| 102 |
+
|
| 103 |
+
## 🔧 Configuração
|
| 104 |
+
|
| 105 |
+
O sistema pode ser configurado através do arquivo `config/config.py`:
|
| 106 |
+
|
| 107 |
+
```python
|
| 108 |
+
# Configurações dos modelos
|
| 109 |
+
MODEL_WEIGHTS = {
|
| 110 |
+
'finbert': 0.4,
|
| 111 |
+
'roberta': 0.35,
|
| 112 |
+
'custom': 0.25
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
# Configurações de análise
|
| 116 |
+
SWING_DETECTION = {
|
| 117 |
+
'lookback_period': 5,
|
| 118 |
+
'min_strength': 0.3
|
| 119 |
+
}
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
## 📊 Exemplos de Uso
|
| 123 |
+
|
| 124 |
+
### Análise de Sentimento
|
| 125 |
+
```python
|
| 126 |
+
from src.analysis.sentiment_analysis import SentimentAnalyzer
|
| 127 |
+
|
| 128 |
+
analyzer = SentimentAnalyzer()
|
| 129 |
+
result = analyzer.analyze_text("O mercado está em alta com perspectivas positivas")
|
| 130 |
+
print(f"Sentimento: {result['sentiment']} (Confiança: {result['confidence']:.2f})")
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
### Análise de Mercado
|
| 134 |
+
```python
|
| 135 |
+
from src.core.advanced_market_processing import AdvancedMarketProcessor
|
| 136 |
+
|
| 137 |
+
processor = AdvancedMarketProcessor()
|
| 138 |
+
result = processor.process_market_data(prices, volumes)
|
| 139 |
+
print(f"Estrutura do mercado: {result['market_structure']}")
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
## 🤝 Contribuição
|
| 143 |
+
|
| 144 |
+
1. Fork o projeto
|
| 145 |
+
2. Crie uma branch para sua feature (`git checkout -b feature/AmazingFeature`)
|
| 146 |
+
3. Commit suas mudanças (`git commit -m 'Add some AmazingFeature'`)
|
| 147 |
+
4. Push para a branch (`git push origin feature/AmazingFeature`)
|
| 148 |
+
5. Abra um Pull Request
|
| 149 |
+
|
| 150 |
+
## 📄 Licença
|
| 151 |
+
|
| 152 |
+
Este projeto está licenciado sob a Licença MIT - veja o arquivo [LICENSE](LICENSE) para detalhes.
|
| 153 |
+
|
| 154 |
+
## 🆘 Suporte
|
| 155 |
+
|
| 156 |
+
Para suporte e dúvidas:
|
| 157 |
+
- Consulte a [documentação](docs/)
|
| 158 |
+
- Abra uma [issue](../../issues)
|
| 159 |
+
- Consulte o [guia de troubleshooting](docs/troubleshooting.md)
|
| 160 |
+
|
| 161 |
+
## 📈 Status do Projeto
|
| 162 |
+
|
| 163 |
+
- ✅ Sistema Ensemble de IA implementado
|
| 164 |
+
- ✅ Análise de mercado avançada
|
| 165 |
+
- ✅ Sistema de logging completo
|
| 166 |
+
- ✅ Interface web funcional
|
| 167 |
+
- ✅ Documentação atualizada
|
| 168 |
+
- ✅ Estrutura refatorada
|
| 169 |
+
|
| 170 |
+
---
|
| 171 |
+
|
| 172 |
+
**Desenvolvido com ❤️ para análise financeira inteligente**
|
__pycache__/app.cpython-313.pyc
ADDED
|
Binary file (46.8 kB). View file
|
|
|
app.py
CHANGED
|
@@ -15,6 +15,7 @@ try:
|
|
| 15 |
from src.analysis.fibonacci_analysis import AdvancedFibonacciEngine
|
| 16 |
from src.integrations.real_time_integration import RealTimeIntegration, BotEvent
|
| 17 |
from src.core.performance_monitor import PerformanceMonitor, measure_analysis_time
|
|
|
|
| 18 |
except ImportError:
|
| 19 |
# Fallback para modo standalone se módulos não existirem
|
| 20 |
print("⚠️ Módulos refatorados não encontrados. Executando em modo standalone.")
|
|
@@ -31,6 +32,9 @@ except ImportError:
|
|
| 31 |
BotEvent = None
|
| 32 |
PerformanceMonitor = None
|
| 33 |
measure_analysis_time = None
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
# Engines de análise
|
| 36 |
technical_engine = None
|
|
@@ -51,6 +55,11 @@ def initialize_engines():
|
|
| 51 |
global technical_engine, sentiment_engine, model_info
|
| 52 |
|
| 53 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
if TechnicalAnalysisEngine and SentimentAnalysisEngine:
|
| 55 |
# Inicializar engine de análise técnica
|
| 56 |
technical_engine = TechnicalAnalysisEngine()
|
|
@@ -180,6 +189,7 @@ def parse_market_data(text):
|
|
| 180 |
except Exception as e:
|
| 181 |
return None
|
| 182 |
|
|
|
|
| 183 |
def analyze_sentiment(text):
|
| 184 |
"""Analisa o sentimento do texto usando engines ou fallback."""
|
| 185 |
if sentiment_engine and hasattr(sentiment_engine, 'analyze_sentiment'):
|
|
@@ -239,6 +249,7 @@ def analyze_sentiment(text):
|
|
| 239 |
'label': 'NEUTRO'
|
| 240 |
}
|
| 241 |
|
|
|
|
| 242 |
def analyze_scalping_signals(market_data, original_text=""):
|
| 243 |
"""Analisa sinais para scalping usando engines ou fallback."""
|
| 244 |
if technical_engine and hasattr(technical_engine, 'analyze_scalping_signals'):
|
|
@@ -489,6 +500,7 @@ def generate_trading_response(analysis):
|
|
| 489 |
|
| 490 |
return response
|
| 491 |
|
|
|
|
| 492 |
def process_trading_analysis(text):
|
| 493 |
"""Função principal que processa a análise de trading usando engines ou fallback."""
|
| 494 |
|
|
@@ -579,6 +591,7 @@ def process_trading_analysis_basic(text):
|
|
| 579 |
return generate_trading_response(analysis)
|
| 580 |
|
| 581 |
# Função principal de análise para a interface
|
|
|
|
| 582 |
def main_analysis_function(text: str) -> str:
|
| 583 |
"""Função principal de análise que será usada pela interface."""
|
| 584 |
result = process_trading_analysis(text)
|
|
|
|
| 15 |
from src.analysis.fibonacci_analysis import AdvancedFibonacciEngine
|
| 16 |
from src.integrations.real_time_integration import RealTimeIntegration, BotEvent
|
| 17 |
from src.core.performance_monitor import PerformanceMonitor, measure_analysis_time
|
| 18 |
+
from src.utils.request_logger import log_requests_responses, enable_logging, disable_logging
|
| 19 |
except ImportError:
|
| 20 |
# Fallback para modo standalone se módulos não existirem
|
| 21 |
print("⚠️ Módulos refatorados não encontrados. Executando em modo standalone.")
|
|
|
|
| 32 |
BotEvent = None
|
| 33 |
PerformanceMonitor = None
|
| 34 |
measure_analysis_time = None
|
| 35 |
+
log_requests_responses = None
|
| 36 |
+
enable_logging = None
|
| 37 |
+
disable_logging = None
|
| 38 |
|
| 39 |
# Engines de análise
|
| 40 |
technical_engine = None
|
|
|
|
| 55 |
global technical_engine, sentiment_engine, model_info
|
| 56 |
|
| 57 |
try:
|
| 58 |
+
# Habilitar logging de requisições/respostas
|
| 59 |
+
if enable_logging:
|
| 60 |
+
enable_logging()
|
| 61 |
+
print("🔍 Sistema de logging de requisições habilitado")
|
| 62 |
+
|
| 63 |
if TechnicalAnalysisEngine and SentimentAnalysisEngine:
|
| 64 |
# Inicializar engine de análise técnica
|
| 65 |
technical_engine = TechnicalAnalysisEngine()
|
|
|
|
| 189 |
except Exception as e:
|
| 190 |
return None
|
| 191 |
|
| 192 |
+
@log_requests_responses("analyze_sentiment") if log_requests_responses else lambda f: f
|
| 193 |
def analyze_sentiment(text):
|
| 194 |
"""Analisa o sentimento do texto usando engines ou fallback."""
|
| 195 |
if sentiment_engine and hasattr(sentiment_engine, 'analyze_sentiment'):
|
|
|
|
| 249 |
'label': 'NEUTRO'
|
| 250 |
}
|
| 251 |
|
| 252 |
+
@measure_analysis_time if measure_analysis_time else lambda f: f
|
| 253 |
def analyze_scalping_signals(market_data, original_text=""):
|
| 254 |
"""Analisa sinais para scalping usando engines ou fallback."""
|
| 255 |
if technical_engine and hasattr(technical_engine, 'analyze_scalping_signals'):
|
|
|
|
| 500 |
|
| 501 |
return response
|
| 502 |
|
| 503 |
+
@log_requests_responses("process_trading_analysis") if log_requests_responses else lambda f: f
|
| 504 |
def process_trading_analysis(text):
|
| 505 |
"""Função principal que processa a análise de trading usando engines ou fallback."""
|
| 506 |
|
|
|
|
| 591 |
return generate_trading_response(analysis)
|
| 592 |
|
| 593 |
# Função principal de análise para a interface
|
| 594 |
+
@log_requests_responses("main_analysis_function") if log_requests_responses else lambda f: f
|
| 595 |
def main_analysis_function(text: str) -> str:
|
| 596 |
"""Função principal de análise que será usada pela interface."""
|
| 597 |
result = process_trading_analysis(text)
|
config.py
DELETED
|
@@ -1,209 +0,0 @@
|
|
| 1 |
-
"""Configurações e constantes do sistema de análise de trading."""
|
| 2 |
-
|
| 3 |
-
from typing import Dict, List, Any
|
| 4 |
-
|
| 5 |
-
# Configurações de modelos de IA
|
| 6 |
-
FINANCIAL_MODELS = [
|
| 7 |
-
{
|
| 8 |
-
"name": "ProsusAI/finbert",
|
| 9 |
-
"description": "FinBERT - Modelo especializado em sentimento financeiro",
|
| 10 |
-
"labels": {"LABEL_0": "NEGATIVO", "LABEL_1": "NEUTRO", "LABEL_2": "POSITIVO"}
|
| 11 |
-
},
|
| 12 |
-
{
|
| 13 |
-
"name": "mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis",
|
| 14 |
-
"description": "DistilRoBERTa - Modelo leve para notícias financeiras",
|
| 15 |
-
"labels": {"LABEL_0": "NEGATIVO", "LABEL_1": "NEUTRO", "LABEL_2": "POSITIVO"}
|
| 16 |
-
},
|
| 17 |
-
{
|
| 18 |
-
"name": "soleimanian/financial-roberta-large-sentiment",
|
| 19 |
-
"description": "Financial RoBERTa - Modelo robusto para textos financeiros",
|
| 20 |
-
"labels": {"LABEL_0": "NEGATIVO", "LABEL_1": "NEUTRO", "LABEL_2": "POSITIVO"}
|
| 21 |
-
},
|
| 22 |
-
{
|
| 23 |
-
"name": "cardiffnlp/twitter-roberta-base-sentiment-latest",
|
| 24 |
-
"description": "RoBERTa - Modelo geral de sentimento (fallback)",
|
| 25 |
-
"labels": {"LABEL_0": "NEGATIVO", "LABEL_1": "NEUTRO", "LABEL_2": "POSITIVO"}
|
| 26 |
-
}
|
| 27 |
-
]
|
| 28 |
-
|
| 29 |
-
# Configurações de análise técnica
|
| 30 |
-
class TechnicalAnalysisConfig:
|
| 31 |
-
"""Configurações para análise técnica."""
|
| 32 |
-
|
| 33 |
-
# RSI thresholds
|
| 34 |
-
RSI_OVERSOLD = 30
|
| 35 |
-
RSI_OVERBOUGHT = 70
|
| 36 |
-
RSI_EXTREME_OVERSOLD = 25
|
| 37 |
-
RSI_EXTREME_OVERBOUGHT = 75
|
| 38 |
-
RSI_NEUTRAL_MIN = 45
|
| 39 |
-
RSI_NEUTRAL_MAX = 55
|
| 40 |
-
|
| 41 |
-
# Bollinger Bands positions
|
| 42 |
-
BB_POSITIONS = {
|
| 43 |
-
'ABAIXO': 'below',
|
| 44 |
-
'ACIMA': 'above',
|
| 45 |
-
'SOBRE': 'above',
|
| 46 |
-
'DENTRO': 'inside'
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
# EMA trends
|
| 50 |
-
EMA_TRENDS = {
|
| 51 |
-
'ALTA': 'up',
|
| 52 |
-
'BAIXA': 'down',
|
| 53 |
-
'NEUTRO': 'neutral'
|
| 54 |
-
}
|
| 55 |
-
|
| 56 |
-
# Volume thresholds
|
| 57 |
-
VOLUME_HIGH_THRESHOLD = 1.0
|
| 58 |
-
VOLUME_LOW_THRESHOLD = 0.5
|
| 59 |
-
|
| 60 |
-
# Momentum thresholds
|
| 61 |
-
SIGNIFICANT_MOVEMENT_THRESHOLD = 0.05 # 5%
|
| 62 |
-
|
| 63 |
-
# Configurações de scoring
|
| 64 |
-
class ScoringConfig:
|
| 65 |
-
"""Configurações para sistema de pontuação."""
|
| 66 |
-
|
| 67 |
-
# Pontuações base
|
| 68 |
-
RSI_SCORE = 25
|
| 69 |
-
EMA_SCORE = 15
|
| 70 |
-
BB_SCORE = 20
|
| 71 |
-
MOMENTUM_SCORE = 10
|
| 72 |
-
VOLUME_SCORE = 10
|
| 73 |
-
SENTIMENT_MAX_SCORE = 20
|
| 74 |
-
|
| 75 |
-
# Bonificações especiais
|
| 76 |
-
PERFECT_SETUP_BONUS = 35
|
| 77 |
-
STRONG_REVERSAL_BONUS = 30
|
| 78 |
-
|
| 79 |
-
# Penalidades
|
| 80 |
-
CONFLICT_PENALTY = 10
|
| 81 |
-
LOW_VOLUME_PENALTY = 5
|
| 82 |
-
|
| 83 |
-
# Limites de confiança
|
| 84 |
-
MAX_CONFIDENCE = 95
|
| 85 |
-
MIN_CONFIDENCE = 10
|
| 86 |
-
|
| 87 |
-
# Configurações de trading
|
| 88 |
-
class TradingConfig:
|
| 89 |
-
"""Configurações para recomendações de trading."""
|
| 90 |
-
|
| 91 |
-
# Risk management
|
| 92 |
-
STOP_LOSS_PERCENTAGE = 0.0007 # 0.07%
|
| 93 |
-
TAKE_PROFIT_PERCENTAGE = 0.0015 # 0.15%
|
| 94 |
-
RISK_REWARD_RATIO = 2 # 1:2
|
| 95 |
-
|
| 96 |
-
# Timeframes
|
| 97 |
-
SCALPING_TIMEFRAMES = ['M1', 'M5']
|
| 98 |
-
|
| 99 |
-
# Confidence levels
|
| 100 |
-
CONFIDENCE_LEVELS = {
|
| 101 |
-
'MUITO_ALTA': 80,
|
| 102 |
-
'ALTA': 65,
|
| 103 |
-
'MODERADA': 50,
|
| 104 |
-
'BAIXA': 0
|
| 105 |
-
}
|
| 106 |
-
|
| 107 |
-
# Configurações de interface
|
| 108 |
-
class UIConfig:
|
| 109 |
-
"""Configurações para interface do usuário."""
|
| 110 |
-
|
| 111 |
-
# Emojis para ações
|
| 112 |
-
ACTION_EMOJIS = {
|
| 113 |
-
'COMPRAR': {'main': '🟢', 'action': '📈'},
|
| 114 |
-
'VENDER': {'main': '🔴', 'action': '📉'},
|
| 115 |
-
'AGUARDAR': {'main': '🟡', 'action': '⏸️'}
|
| 116 |
-
}
|
| 117 |
-
|
| 118 |
-
# Emojis para sentimento
|
| 119 |
-
SENTIMENT_EMOJIS = {
|
| 120 |
-
'POSITIVO': '😊💚',
|
| 121 |
-
'NEGATIVO': '😟💔',
|
| 122 |
-
'NEUTRO': '😐💛'
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
# Cores para ações
|
| 126 |
-
ACTION_COLORS = {
|
| 127 |
-
'COMPRAR': 'verde',
|
| 128 |
-
'VENDER': 'vermelho',
|
| 129 |
-
'AGUARDAR': 'amarelo'
|
| 130 |
-
}
|
| 131 |
-
|
| 132 |
-
# Direções de trading
|
| 133 |
-
TRADING_DIRECTIONS = {
|
| 134 |
-
'COMPRAR': 'LONG',
|
| 135 |
-
'VENDER': 'SHORT',
|
| 136 |
-
'AGUARDAR': 'NEUTRO'
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
# Configurações de regex para parsing
|
| 140 |
-
class RegexPatterns:
|
| 141 |
-
"""Padrões regex para extração de dados."""
|
| 142 |
-
|
| 143 |
-
PRICE_PATTERN = r'P:([0-9,]+\.?\d*)'
|
| 144 |
-
VARIATION_PATTERN = r'\(([\+\-]\d+\.?\d*%?)\)'
|
| 145 |
-
RSI_PATTERN = r'RSI:(\d+)'
|
| 146 |
-
EMA_PATTERN = r'EMA:(ALTA|BAIXA)'
|
| 147 |
-
BB_PATTERN = r'BB:(DENTRO|SOBRE|ABAIXO|ACIMA)'
|
| 148 |
-
VOLUME_PATTERN = r'Vol:([0-9\.]+)'
|
| 149 |
-
|
| 150 |
-
# Configurações de IA
|
| 151 |
-
class AIConfig:
|
| 152 |
-
"""Configurações para análise de IA."""
|
| 153 |
-
|
| 154 |
-
# Tamanho máximo de texto para análise
|
| 155 |
-
MAX_TEXT_LENGTH = 512
|
| 156 |
-
|
| 157 |
-
# Configurações do pipeline
|
| 158 |
-
PIPELINE_CONFIG = {
|
| 159 |
-
'task': 'sentiment-analysis',
|
| 160 |
-
'return_all_scores': True
|
| 161 |
-
}
|
| 162 |
-
|
| 163 |
-
# Mapeamento genérico de labels
|
| 164 |
-
GENERIC_LABEL_MAPPING = {
|
| 165 |
-
'negative': 'NEGATIVO',
|
| 166 |
-
'positive': 'POSITIVO',
|
| 167 |
-
'neutral': 'NEUTRO'
|
| 168 |
-
}
|
| 169 |
-
|
| 170 |
-
# Configurações gerais da aplicação
|
| 171 |
-
class AppConfig:
|
| 172 |
-
"""Configurações gerais da aplicação."""
|
| 173 |
-
|
| 174 |
-
# Informações da aplicação
|
| 175 |
-
APP_TITLE = "🧛♂️ VAMPIRE SCALPING BOT"
|
| 176 |
-
APP_SUBTITLE = "Análise Técnica + IA Financeira para WIN M1/M5"
|
| 177 |
-
APP_DESCRIPTION = "Sistema avançado de análise para scalping com FinBERT e indicadores técnicos"
|
| 178 |
-
|
| 179 |
-
# Configurações do servidor
|
| 180 |
-
DEFAULT_HOST = "127.0.0.1"
|
| 181 |
-
DEFAULT_PORT = 7860
|
| 182 |
-
|
| 183 |
-
# Configurações de logging
|
| 184 |
-
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
| 185 |
-
|
| 186 |
-
# Mensagens de status
|
| 187 |
-
STATUS_MESSAGES = {
|
| 188 |
-
'AI_UNAVAILABLE': '⚠️ Transformers não disponível. Executando sem IA.',
|
| 189 |
-
'AI_LOADING': '🔄 Tentando carregar: {}',
|
| 190 |
-
'AI_SUCCESS': '✅ {} carregado com sucesso!',
|
| 191 |
-
'AI_FAILED': '❌ Falha ao carregar {}: {}',
|
| 192 |
-
'NO_MODEL_LOADED': '❌ Nenhum modelo de sentimento pôde ser carregado.'
|
| 193 |
-
}
|
| 194 |
-
|
| 195 |
-
# Configurações de exemplo de dados
|
| 196 |
-
class ExampleData:
|
| 197 |
-
"""Dados de exemplo para testes e demonstração."""
|
| 198 |
-
|
| 199 |
-
SAMPLE_MARKET_DATA = {
|
| 200 |
-
'bullish': 'P:134,500(+0.85%) | EMA:ALTA | RSI:35 | BB:ABAIXO | Vol:1.2',
|
| 201 |
-
'bearish': 'P:133,200(-1.20%) | EMA:BAIXA | RSI:75 | BB:ACIMA | Vol:1.5',
|
| 202 |
-
'neutral': 'P:133,850(+0.15%) | EMA:ALTA | RSI:52 | BB:DENTRO | Vol:0.8'
|
| 203 |
-
}
|
| 204 |
-
|
| 205 |
-
SAMPLE_NEWS_TEXT = {
|
| 206 |
-
'positive': 'Mercado em alta com boas perspectivas econômicas',
|
| 207 |
-
'negative': 'Preocupações com inflação afetam mercado',
|
| 208 |
-
'neutral': 'Mercado aguarda dados econômicos'
|
| 209 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/architecture.md
CHANGED
|
@@ -2,36 +2,36 @@
|
|
| 2 |
|
| 3 |
## Visão Geral
|
| 4 |
|
| 5 |
-
O
|
| 6 |
|
| 7 |
## Diagrama de Arquitetura
|
| 8 |
|
| 9 |
```
|
| 10 |
┌─────────────────────────────────────────────────────────────────┐
|
| 11 |
-
│
|
| 12 |
├─────────────────────────────────────────────────────────────────┤
|
| 13 |
│ 📱 PRESENTATION LAYER │
|
| 14 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 15 |
│ │ Gradio UI │ │ REST API │ │ WebSocket │ │
|
| 16 |
-
│ │
|
| 17 |
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 18 |
├─────────────────────────────────────────────────────────────────┤
|
| 19 |
│ 🧠 BUSINESS LOGIC LAYER │
|
| 20 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 21 |
-
│ │
|
| 22 |
-
│ │
|
| 23 |
-
│ │ (
|
| 24 |
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 25 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 26 |
-
│ │
|
| 27 |
-
│ │
|
| 28 |
-
│ │ (
|
| 29 |
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 30 |
├─────────────────────────────────────────────────────────────────┤
|
| 31 |
│ 🔧 INFRASTRUCTURE LAYER │
|
| 32 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 33 |
│ │ Configuration │ │ Utilities │ │ Logging │ │
|
| 34 |
-
│ │ (config
|
| 35 |
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 36 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 37 |
│ │ AI Models │ │ File System │ │ Threading │ │
|
|
@@ -40,311 +40,273 @@ O Vampire Trading Bot é construído com uma arquitetura modular e escalável qu
|
|
| 40 |
└─────────────────────────────────────────────────────────────────┘
|
| 41 |
```
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
## Componentes Principais
|
| 44 |
|
| 45 |
### 1. 📱 Camada de Apresentação
|
| 46 |
|
| 47 |
-
#### Gradio
|
| 48 |
- **Responsabilidade**: Interface web interativa
|
| 49 |
-
- **Tecnologia**: Gradio
|
| 50 |
- **Funcionalidades**:
|
| 51 |
-
-
|
| 52 |
-
-
|
| 53 |
-
-
|
| 54 |
-
-
|
| 55 |
-
- Configurações de usuário
|
| 56 |
-
|
| 57 |
-
```python
|
| 58 |
-
class GradioInterface:
|
| 59 |
-
def __init__(self):
|
| 60 |
-
self.technical_engine = TechnicalAnalysisEngine()
|
| 61 |
-
self.sentiment_engine = SentimentAnalysisEngine()
|
| 62 |
-
self.fibonacci_engine = AdvancedFibonacciEngine()
|
| 63 |
-
|
| 64 |
-
def create_interface(self) -> gr.Blocks:
|
| 65 |
-
# Criação da interface completa
|
| 66 |
-
```
|
| 67 |
|
| 68 |
### 2. 🧠 Camada de Lógica de Negócio
|
| 69 |
|
| 70 |
-
####
|
| 71 |
-
- **Responsabilidade**:
|
| 72 |
-
- **
|
| 73 |
-
-
|
| 74 |
-
-
|
| 75 |
-
-
|
| 76 |
-
|
| 77 |
-
-
|
| 78 |
-
-
|
| 79 |
-
|
| 80 |
-
```python
|
| 81 |
-
class TechnicalAnalysisEngine:
|
| 82 |
-
def __init__(self):
|
| 83 |
-
self.rsi_analyzer = RSIAnalyzer()
|
| 84 |
-
self.ema_analyzer = EMAAnalyzer()
|
| 85 |
-
self.bollinger_analyzer = BollingerAnalyzer()
|
| 86 |
-
# ...
|
| 87 |
-
```
|
| 88 |
|
| 89 |
-
####
|
| 90 |
-
- **Responsabilidade**: Análise
|
| 91 |
-
- **
|
| 92 |
-
-
|
| 93 |
-
-
|
| 94 |
-
-
|
| 95 |
-
-
|
| 96 |
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
|
| 105 |
-
####
|
| 106 |
-
- **Responsabilidade**:
|
| 107 |
- **Funcionalidades**:
|
| 108 |
-
-
|
| 109 |
-
-
|
| 110 |
-
-
|
| 111 |
-
- Projeções temporais
|
| 112 |
-
- Análise de força dos níveis
|
| 113 |
|
| 114 |
-
|
| 115 |
-
class AdvancedFibonacciEngine:
|
| 116 |
-
def perform_advanced_analysis(
|
| 117 |
-
self, swing_high: float, swing_low: float, current_price: float
|
| 118 |
-
) -> AdvancedFibonacciAnalysis:
|
| 119 |
-
# Análise completa de Fibonacci
|
| 120 |
-
```
|
| 121 |
|
| 122 |
-
####
|
| 123 |
-
- **Responsabilidade**:
|
| 124 |
-
- **Componentes**:
|
| 125 |
-
- `FileWatcher`: Monitoramento de arquivos
|
| 126 |
-
- `RealTimeProcessor`: Processamento de eventos
|
| 127 |
-
- `BotEvent`: Estrutura de eventos
|
| 128 |
-
|
| 129 |
-
#### Performance Monitor (`performance_monitor.py`)
|
| 130 |
-
- **Responsabilidade**: Monitoramento de performance
|
| 131 |
-
- **Métricas**:
|
| 132 |
-
- CPU e memória
|
| 133 |
-
- Tempo de análise
|
| 134 |
-
- Contadores de eventos
|
| 135 |
-
- Estatísticas de precisão
|
| 136 |
-
|
| 137 |
-
#### Log Parser (`log_parser.py`)
|
| 138 |
-
- **Responsabilidade**: Parsing de logs do bot
|
| 139 |
- **Funcionalidades**:
|
| 140 |
-
-
|
| 141 |
-
-
|
| 142 |
-
-
|
|
|
|
| 143 |
|
| 144 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
|
| 146 |
-
####
|
| 147 |
-
- **Responsabilidade**:
|
| 148 |
-
- **
|
| 149 |
-
-
|
| 150 |
-
-
|
| 151 |
-
-
|
| 152 |
-
-
|
| 153 |
-
|
| 154 |
-
#### Utilities (`utils.py`)
|
| 155 |
-
- **Responsabilidade**: Funções auxiliares
|
| 156 |
-
- **Utilitários**:
|
| 157 |
-
- `DateTimeUtils`: Manipulação de datas
|
| 158 |
-
- `NumberUtils`: Formatação de números
|
| 159 |
-
- `ValidationUtils`: Validação de dados
|
| 160 |
-
- `LogUtils`: Utilitários de logging
|
| 161 |
|
| 162 |
## Fluxo de Dados
|
| 163 |
|
| 164 |
-
### 1.
|
| 165 |
-
|
| 166 |
```
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
│ └── Detecção de setups
|
| 177 |
-
├── Análise de Sentimento
|
| 178 |
-
│ ├── Pré-processamento
|
| 179 |
-
│ ├── Inferência do modelo
|
| 180 |
-
│ └── Pontuação
|
| 181 |
-
└── Análise de Fibonacci
|
| 182 |
-
├── Cálculo de níveis
|
| 183 |
-
├── Zonas de confluência
|
| 184 |
-
└── Padrões harmônicos
|
| 185 |
-
|
| 186 |
-
3. Consolidação
|
| 187 |
-
├── Combinação de resultados
|
| 188 |
-
├── Cálculo de confiança
|
| 189 |
-
└── Geração de sinais
|
| 190 |
-
|
| 191 |
-
4. Apresentação
|
| 192 |
-
├── Formatação de resultados
|
| 193 |
-
├── Geração de gráficos
|
| 194 |
-
└── Exibição na UI
|
| 195 |
```
|
| 196 |
|
| 197 |
-
### 2.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
|
|
|
|
| 199 |
```
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
```
|
| 204 |
|
| 205 |
## Padrões de Design Utilizados
|
| 206 |
|
| 207 |
### 1. **Strategy Pattern**
|
| 208 |
-
-
|
| 209 |
-
-
|
| 210 |
-
- Estratégias de formatação
|
| 211 |
|
| 212 |
-
### 2. **
|
| 213 |
-
-
|
| 214 |
-
-
|
| 215 |
-
- Atualizações de UI
|
| 216 |
|
| 217 |
-
### 3. **
|
| 218 |
-
-
|
| 219 |
-
-
|
| 220 |
-
- Geração de interfaces
|
| 221 |
|
| 222 |
### 4. **Singleton Pattern**
|
| 223 |
-
-
|
| 224 |
-
-
|
| 225 |
-
- Cache de modelos
|
| 226 |
-
|
| 227 |
-
### 5. **Facade Pattern**
|
| 228 |
-
- Engines principais
|
| 229 |
-
- Interface unificada
|
| 230 |
-
- Simplificação de APIs
|
| 231 |
-
|
| 232 |
-
## Estruturas de Dados
|
| 233 |
-
|
| 234 |
-
### Core Data Classes
|
| 235 |
-
|
| 236 |
-
```python
|
| 237 |
-
@dataclass
|
| 238 |
-
class MarketData:
|
| 239 |
-
symbol: str
|
| 240 |
-
current_price: float
|
| 241 |
-
variation: float
|
| 242 |
-
high: float
|
| 243 |
-
low: float
|
| 244 |
-
volume: int
|
| 245 |
-
timestamp: str
|
| 246 |
-
|
| 247 |
-
@dataclass
|
| 248 |
-
class TechnicalSignal:
|
| 249 |
-
indicator: str
|
| 250 |
-
value: float
|
| 251 |
-
signal: str
|
| 252 |
-
confidence: float
|
| 253 |
-
timestamp: str
|
| 254 |
-
|
| 255 |
-
@dataclass
|
| 256 |
-
class SentimentResult:
|
| 257 |
-
text: str
|
| 258 |
-
sentiment: str
|
| 259 |
-
confidence: float
|
| 260 |
-
scores: Dict[str, float]
|
| 261 |
-
keywords: List[str]
|
| 262 |
-
```
|
| 263 |
-
|
| 264 |
-
## Extensibilidade
|
| 265 |
-
|
| 266 |
-
### Adicionando Novos Indicadores
|
| 267 |
|
| 268 |
-
|
| 269 |
-
2. Implementar método `analyze()`
|
| 270 |
-
3. Registrar no `TechnicalAnalysisEngine`
|
| 271 |
|
| 272 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
|
|
|
|
|
|
| 277 |
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
1. Implementar interface seguindo padrão
|
| 281 |
-
2. Integrar com engines existentes
|
| 282 |
-
3. Configurar roteamento
|
| 283 |
|
| 284 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
|
| 286 |
-
|
| 287 |
-
- Cache de modelos de IA
|
| 288 |
-
- Cache de cálculos técnicos
|
| 289 |
-
- Cache de resultados de análise
|
| 290 |
|
| 291 |
-
###
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
|
|
|
| 295 |
|
| 296 |
-
|
| 297 |
-
- Lazy loading de modelos
|
| 298 |
-
- Limpeza automática de cache
|
| 299 |
-
- Monitoramento de uso de memória
|
| 300 |
|
| 301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
|
| 303 |
-
###
|
| 304 |
-
-
|
| 305 |
-
-
|
| 306 |
-
-
|
|
|
|
|
|
|
| 307 |
|
| 308 |
-
|
| 309 |
-
- Try-catch abrangente
|
| 310 |
-
- Fallbacks para modo standalone
|
| 311 |
-
- Logging detalhado de erros
|
| 312 |
|
| 313 |
-
###
|
| 314 |
-
-
|
| 315 |
-
-
|
| 316 |
-
-
|
| 317 |
|
| 318 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
-
|
| 321 |
-
- Performance do sistema
|
| 322 |
-
- Tempo de resposta
|
| 323 |
-
- Taxa de sucesso/erro
|
| 324 |
-
- Uso de recursos
|
| 325 |
-
|
| 326 |
-
### Logging
|
| 327 |
-
- Logs estruturados
|
| 328 |
-
- Níveis de log configuráveis
|
| 329 |
-
- Rotação automática
|
| 330 |
-
|
| 331 |
-
### Alertas
|
| 332 |
-
- Alertas de performance
|
| 333 |
-
- Notificações de erro
|
| 334 |
-
- Métricas de negócio
|
| 335 |
-
|
| 336 |
-
## Próximos Passos da Arquitetura
|
| 337 |
-
|
| 338 |
-
### Melhorias Planejadas
|
| 339 |
-
1. **Microserviços**: Separação em serviços independentes
|
| 340 |
-
2. **API REST**: Interface programática
|
| 341 |
-
3. **WebSocket**: Comunicação em tempo real
|
| 342 |
-
4. **Database**: Persistência de dados
|
| 343 |
-
5. **Docker**: Containerização
|
| 344 |
-
6. **CI/CD**: Pipeline de deployment
|
| 345 |
-
|
| 346 |
-
### Escalabilidade
|
| 347 |
-
1. **Load Balancing**: Distribuição de carga
|
| 348 |
-
2. **Caching Distribuído**: Redis/Memcached
|
| 349 |
-
3. **Message Queue**: RabbitMQ/Kafka
|
| 350 |
-
4. **Horizontal Scaling**: Múltiplas instâncias
|
|
|
|
| 2 |
|
| 3 |
## Visão Geral
|
| 4 |
|
| 5 |
+
O Sistema de Análise Financeira com IA é construído com uma arquitetura modular e escalável que separa responsabilidades e permite fácil manutenção e extensão. O sistema segue princípios de design limpo e padrões de arquitetura bem estabelecidos.
|
| 6 |
|
| 7 |
## Diagrama de Arquitetura
|
| 8 |
|
| 9 |
```
|
| 10 |
┌─────────────────────────────────────────────────────────────────┐
|
| 11 |
+
│ SISTEMA DE ANÁLISE FINANCEIRA COM IA │
|
| 12 |
├─────────────────────────────────────────────────────────────────┤
|
| 13 |
│ 📱 PRESENTATION LAYER │
|
| 14 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 15 |
│ │ Gradio UI │ │ REST API │ │ WebSocket │ │
|
| 16 |
+
│ │ (ui/gradio_*) │ │ (future) │ │ (future) │ │
|
| 17 |
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 18 |
├─────────────────────────────────────────────────────────────────┤
|
| 19 |
│ 🧠 BUSINESS LOGIC LAYER │
|
| 20 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 21 |
+
│ │ Ensemble AI │ │ Market Analysis │ │ Sentiment │ │
|
| 22 |
+
│ │ System │ │ Engine │ │ Analysis │ │
|
| 23 |
+
│ │ (ai/ensemble_*) │ │ (analysis/*) │ │ (analysis/*) │ │
|
| 24 |
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 25 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 26 |
+
│ │ Advanced Market │ │ Performance │ │ Request Logger │ │
|
| 27 |
+
│ │ Processing │ │ Monitor │ │ (utils/*) │ │
|
| 28 |
+
│ │ (core/*) │ │ (integrations/) │ │ │ │
|
| 29 |
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 30 |
├─────────────────────────────────────────────────────────────────┤
|
| 31 |
│ 🔧 INFRASTRUCTURE LAYER │
|
| 32 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 33 |
│ │ Configuration │ │ Utilities │ │ Logging │ │
|
| 34 |
+
│ │ (config/) │ │ (utils/) │ │ (built-in) │ │
|
| 35 |
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 36 |
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 37 |
│ │ AI Models │ │ File System │ │ Threading │ │
|
|
|
|
| 40 |
└─────────────────────────────────────────────────────────────────┘
|
| 41 |
```
|
| 42 |
|
| 43 |
+
## Estrutura de Diretórios
|
| 44 |
+
|
| 45 |
+
```
|
| 46 |
+
📁 Projeto/
|
| 47 |
+
├── 📄 app.py # Aplicação principal
|
| 48 |
+
├── 📄 requirements.txt # Dependências
|
| 49 |
+
├── 📄 README.md # Documentação principal
|
| 50 |
+
├── 📄 CHANGELOG.md # Histórico de mudanças
|
| 51 |
+
│
|
| 52 |
+
├── 📁 src/ # Código fonte organizado
|
| 53 |
+
│ ├── 📁 ai/ # Modelos de IA e ensemble
|
| 54 |
+
│ │ ├── 📄 __init__.py
|
| 55 |
+
│ │ ├── 📄 ensemble_ai.py # Sistema ensemble principal
|
| 56 |
+
│ │ └── 📄 model_manager.py # Gerenciamento de modelos
|
| 57 |
+
│ │
|
| 58 |
+
│ ├── 📁 analysis/ # Análises especializadas
|
| 59 |
+
│ │ ├── 📄 __init__.py
|
| 60 |
+
│ │ ├── 📄 market_analysis.py # Análise de mercado
|
| 61 |
+
│ │ └── 📄 sentiment_analysis.py # Análise de sentimento
|
| 62 |
+
│ │
|
| 63 |
+
│ ├── 📁 core/ # Funcionalidades centrais
|
| 64 |
+
│ │ ├── 📄 __init__.py
|
| 65 |
+
│ │ ├── 📄 advanced_market_processing.py # Processamento avançado
|
| 66 |
+
│ │ └── 📄 data_processor.py # Processamento de dados
|
| 67 |
+
│ │
|
| 68 |
+
│ ├── 📁 integrations/ # Integrações externas
|
| 69 |
+
│ │ ├── 📄 __init__.py
|
| 70 |
+
│ │ ├── 📄 performance_monitor.py # Monitor de performance
|
| 71 |
+
│ │ └── 📄 real_time_integration.py # Integração tempo real
|
| 72 |
+
│ │
|
| 73 |
+
│ ├── 📁 ui/ # Interface do usuário
|
| 74 |
+
│ │ ├── 📄 __init__.py
|
| 75 |
+
│ │ └── 📄 gradio_interface.py # Interface Gradio
|
| 76 |
+
│ │
|
| 77 |
+
│ └── 📁 utils/ # Utilitários
|
| 78 |
+
│ ├── 📄 __init__.py
|
| 79 |
+
│ ├── 📄 helpers.py # Funções auxiliares
|
| 80 |
+
│ └── 📄 request_logger.py # Logger de requisições
|
| 81 |
+
│
|
| 82 |
+
├── 📁 config/ # Configurações
|
| 83 |
+
│ ├── 📄 __init__.py
|
| 84 |
+
│ └── 📄 config.py # Configurações principais
|
| 85 |
+
│
|
| 86 |
+
├── 📁 docs/ # Documentação
|
| 87 |
+
│ ├── 📄 api-reference.md # Referência da API
|
| 88 |
+
│ ├── 📄 architecture.md # Arquitetura do sistema
|
| 89 |
+
│ ├── 📄 developer-guide.md # Guia do desenvolvedor
|
| 90 |
+
│ ├── 📄 installation.md # Guia de instalação
|
| 91 |
+
│ └── 📄 troubleshooting.md # Solução de problemas
|
| 92 |
+
│
|
| 93 |
+
├── 📁 tests/ # Testes automatizados
|
| 94 |
+
│ ├── 📄 __init__.py
|
| 95 |
+
│ ├── 📄 conftest.py # Configurações de teste
|
| 96 |
+
│ ├── 📄 test_ensemble_ai.py # Testes do ensemble
|
| 97 |
+
│ ├── 📄 test_market_analysis.py # Testes de análise
|
| 98 |
+
│ └── 📄 test_sentiment_analysis.py # Testes de sentimento
|
| 99 |
+
│
|
| 100 |
+
├── 📁 logs/ # Arquivos de log
|
| 101 |
+
├── 📁 models/ # Modelos salvos
|
| 102 |
+
├── 📁 cache/ # Cache de dados
|
| 103 |
+
└── 📁 __pycache__/ # Cache Python
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
## Componentes Principais
|
| 107 |
|
| 108 |
### 1. 📱 Camada de Apresentação
|
| 109 |
|
| 110 |
+
#### Interface Gradio (`src/ui/gradio_interface.py`)
|
| 111 |
- **Responsabilidade**: Interface web interativa
|
| 112 |
+
- **Tecnologia**: Gradio 5.44.0
|
| 113 |
- **Funcionalidades**:
|
| 114 |
+
- Interface de análise de sentimento
|
| 115 |
+
- Visualização de resultados
|
| 116 |
+
- Controles de configuração
|
| 117 |
+
- Monitoramento em tempo real
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
|
| 119 |
### 2. 🧠 Camada de Lógica de Negócio
|
| 120 |
|
| 121 |
+
#### Sistema Ensemble de IA (`src/ai/ensemble_ai.py`)
|
| 122 |
+
- **Responsabilidade**: Coordenação de múltiplos modelos de IA
|
| 123 |
+
- **Modelos Suportados**:
|
| 124 |
+
- FinBERT (ProsusAI/finbert)
|
| 125 |
+
- RoBERTa (cardiffnlp/twitter-roberta-base-sentiment-latest)
|
| 126 |
+
- Modelos customizados
|
| 127 |
+
- **Funcionalidades**:
|
| 128 |
+
- Votação inteligente entre modelos
|
| 129 |
+
- Otimização automática de pesos
|
| 130 |
+
- Ensemble adaptativo
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
|
| 132 |
+
#### Análise de Mercado (`src/analysis/market_analysis.py`)
|
| 133 |
+
- **Responsabilidade**: Análise técnica e fundamental
|
| 134 |
+
- **Funcionalidades**:
|
| 135 |
+
- Indicadores técnicos
|
| 136 |
+
- Análise de tendências
|
| 137 |
+
- Detecção de padrões
|
| 138 |
+
- Cálculos de Fibonacci
|
| 139 |
|
| 140 |
+
#### Processamento Avançado (`src/core/advanced_market_processing.py`)
|
| 141 |
+
- **Responsabilidade**: Análise avançada de estrutura de mercado
|
| 142 |
+
- **Funcionalidades**:
|
| 143 |
+
- Detecção de swing points
|
| 144 |
+
- Análise de zonas de confluência
|
| 145 |
+
- Reconhecimento de padrões harmônicos
|
| 146 |
+
- Análise de estrutura de mercado
|
| 147 |
|
| 148 |
+
#### Análise de Sentimento (`src/analysis/sentiment_analysis.py`)
|
| 149 |
+
- **Responsabilidade**: Processamento de texto financeiro
|
| 150 |
- **Funcionalidades**:
|
| 151 |
+
- Classificação de sentimento (Bullish/Bearish/Neutral)
|
| 152 |
+
- Scores de confiança
|
| 153 |
+
- Análise contextual financeira
|
|
|
|
|
|
|
| 154 |
|
| 155 |
+
### 3. 🔧 Camada de Infraestrutura
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
|
| 157 |
+
#### Sistema de Configuração (`config/config.py`)
|
| 158 |
+
- **Responsabilidade**: Gerenciamento centralizado de configurações
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
- **Funcionalidades**:
|
| 160 |
+
- Configurações de modelos
|
| 161 |
+
- Parâmetros de análise
|
| 162 |
+
- Configurações de logging
|
| 163 |
+
- Variáveis de ambiente
|
| 164 |
|
| 165 |
+
#### Logger de Requisições (`src/utils/request_logger.py`)
|
| 166 |
+
- **Responsabilidade**: Rastreamento detalhado de operações
|
| 167 |
+
- **Funcionalidades**:
|
| 168 |
+
- Log de requisições HTTP
|
| 169 |
+
- Métricas de performance
|
| 170 |
+
- Debugging estruturado
|
| 171 |
+
- Análise de uso
|
| 172 |
|
| 173 |
+
#### Monitor de Performance (`src/integrations/performance_monitor.py`)
|
| 174 |
+
- **Responsabilidade**: Monitoramento de sistema
|
| 175 |
+
- **Funcionalidades**:
|
| 176 |
+
- Métricas de CPU/RAM
|
| 177 |
+
- Tempo de resposta
|
| 178 |
+
- Throughput de modelos
|
| 179 |
+
- Alertas de performance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
|
| 181 |
## Fluxo de Dados
|
| 182 |
|
| 183 |
+
### 1. Análise de Sentimento
|
|
|
|
| 184 |
```
|
| 185 |
+
Texto de Entrada → Pré-processamento → Ensemble AI → Votação → Resultado Final
|
| 186 |
+
↓
|
| 187 |
+
[FinBERT] [RoBERTa] [Custom]
|
| 188 |
+
↓ ↓ ↓
|
| 189 |
+
[Score1] [Score2] [Score3]
|
| 190 |
+
↓ ↓ ↓
|
| 191 |
+
Sistema de Votação Inteligente
|
| 192 |
+
↓
|
| 193 |
+
Resultado Consolidado + Confiança
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
```
|
| 195 |
|
| 196 |
+
### 2. Análise de Mercado
|
| 197 |
+
```
|
| 198 |
+
Dados de Mercado → Processamento → Análise Técnica → Padrões → Insights
|
| 199 |
+
↓ ↓ ↓
|
| 200 |
+
Swing Points → Confluências → Harmônicos
|
| 201 |
+
↓ ↓ ↓
|
| 202 |
+
Níveis Chave → Zonas Críticas → Projeções
|
| 203 |
+
```
|
| 204 |
|
| 205 |
+
### 3. Sistema de Logging
|
| 206 |
```
|
| 207 |
+
Requisição → Request Logger → Processamento → Response Logger → Métricas
|
| 208 |
+
↓ ↓ ↓ ↓ ↓
|
| 209 |
+
Timestamp → Parâmetros → Tempo Execução → Resultado → Dashboard
|
| 210 |
```
|
| 211 |
|
| 212 |
## Padrões de Design Utilizados
|
| 213 |
|
| 214 |
### 1. **Strategy Pattern**
|
| 215 |
+
- Implementado no sistema ensemble para diferentes estratégias de votação
|
| 216 |
+
- Permite trocar algoritmos de consenso dinamicamente
|
|
|
|
| 217 |
|
| 218 |
+
### 2. **Factory Pattern**
|
| 219 |
+
- Usado para criação de diferentes tipos de analisadores
|
| 220 |
+
- Facilita extensão com novos modelos
|
|
|
|
| 221 |
|
| 222 |
+
### 3. **Observer Pattern**
|
| 223 |
+
- Sistema de logging observa operações do sistema
|
| 224 |
+
- Permite monitoramento não-intrusivo
|
|
|
|
| 225 |
|
| 226 |
### 4. **Singleton Pattern**
|
| 227 |
+
- Configurações globais do sistema
|
| 228 |
+
- Gerenciamento de recursos compartilhados
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
|
| 230 |
+
## Escalabilidade e Performance
|
|
|
|
|
|
|
| 231 |
|
| 232 |
+
### Otimizações Implementadas
|
| 233 |
+
1. **Cache de Modelos**: Modelos carregados uma vez e reutilizados
|
| 234 |
+
2. **Processamento Assíncrono**: Operações não-bloqueantes
|
| 235 |
+
3. **Batch Processing**: Processamento em lotes para eficiência
|
| 236 |
+
4. **Memory Management**: Limpeza automática de recursos
|
| 237 |
|
| 238 |
+
### Métricas de Performance
|
| 239 |
+
- **Tempo de Resposta**: < 2s para análise de sentimento
|
| 240 |
+
- **Throughput**: > 100 análises/minuto
|
| 241 |
+
- **Uso de Memória**: < 2GB RAM em operação normal
|
| 242 |
+
- **Precisão**: > 85% em datasets de teste
|
| 243 |
|
| 244 |
+
## Segurança
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
|
| 246 |
+
### Medidas Implementadas
|
| 247 |
+
1. **Validação de Entrada**: Sanitização de todos os inputs
|
| 248 |
+
2. **Rate Limiting**: Prevenção de abuso da API
|
| 249 |
+
3. **Logging Seguro**: Não exposição de dados sensíveis
|
| 250 |
+
4. **Error Handling**: Tratamento seguro de exceções
|
| 251 |
|
| 252 |
+
## Extensibilidade
|
|
|
|
|
|
|
|
|
|
| 253 |
|
| 254 |
+
### Pontos de Extensão
|
| 255 |
+
1. **Novos Modelos**: Interface padronizada para adicionar modelos
|
| 256 |
+
2. **Novos Indicadores**: Sistema plugável de indicadores técnicos
|
| 257 |
+
3. **Novas Integrações**: Framework para conectar APIs externas
|
| 258 |
+
4. **Novos Formatos**: Suporte extensível a diferentes tipos de dados
|
| 259 |
|
| 260 |
+
## Monitoramento e Observabilidade
|
|
|
|
|
|
|
|
|
|
| 261 |
|
| 262 |
+
### Logs Estruturados
|
| 263 |
+
```json
|
| 264 |
+
{
|
| 265 |
+
"timestamp": "2024-01-27T10:30:00Z",
|
| 266 |
+
"level": "INFO",
|
| 267 |
+
"component": "ensemble_ai",
|
| 268 |
+
"operation": "sentiment_analysis",
|
| 269 |
+
"duration_ms": 1250,
|
| 270 |
+
"input_length": 150,
|
| 271 |
+
"model_scores": {
|
| 272 |
+
"finbert": 0.85,
|
| 273 |
+
"roberta": 0.78,
|
| 274 |
+
"custom": 0.82
|
| 275 |
+
},
|
| 276 |
+
"final_result": "bullish",
|
| 277 |
+
"confidence": 0.82
|
| 278 |
+
}
|
| 279 |
+
```
|
| 280 |
|
| 281 |
+
### Métricas Coletadas
|
| 282 |
+
- Latência por componente
|
| 283 |
+
- Taxa de erro por modelo
|
| 284 |
+
- Distribuição de sentimentos
|
| 285 |
+
- Uso de recursos do sistema
|
| 286 |
+
- Padrões de uso da aplicação
|
| 287 |
|
| 288 |
+
## Considerações de Deployment
|
|
|
|
|
|
|
|
|
|
| 289 |
|
| 290 |
+
### Ambientes Suportados
|
| 291 |
+
- **Desenvolvimento**: Local com hot-reload
|
| 292 |
+
- **Staging**: Ambiente de testes
|
| 293 |
+
- **Produção**: Hugging Face Spaces
|
| 294 |
|
| 295 |
+
### Configurações por Ambiente
|
| 296 |
+
```python
|
| 297 |
+
# config/config.py
|
| 298 |
+
ENVIRONMENTS = {
|
| 299 |
+
'development': {
|
| 300 |
+
'debug': True,
|
| 301 |
+
'model_cache': False,
|
| 302 |
+
'logging_level': 'DEBUG'
|
| 303 |
+
},
|
| 304 |
+
'production': {
|
| 305 |
+
'debug': False,
|
| 306 |
+
'model_cache': True,
|
| 307 |
+
'logging_level': 'INFO'
|
| 308 |
+
}
|
| 309 |
+
}
|
| 310 |
+
```
|
| 311 |
|
| 312 |
+
Esta arquitetura garante um sistema robusto, escalável e de fácil manutenção, seguindo as melhores práticas de desenvolvimento de software.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fibonacci_analysis.py
DELETED
|
@@ -1,528 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import pandas as pd
|
| 3 |
-
from typing import Dict, List, Tuple, Optional, Any
|
| 4 |
-
from dataclasses import dataclass, asdict
|
| 5 |
-
import logging
|
| 6 |
-
from log_parser import FibonacciAnalysis, BotAnalysis
|
| 7 |
-
|
| 8 |
-
# Configurar logging
|
| 9 |
-
logging.basicConfig(level=logging.INFO)
|
| 10 |
-
logger = logging.getLogger(__name__)
|
| 11 |
-
|
| 12 |
-
@dataclass
|
| 13 |
-
class FibonacciLevel:
|
| 14 |
-
"""Estrutura para um nível de Fibonacci"""
|
| 15 |
-
level: float
|
| 16 |
-
price: float
|
| 17 |
-
type: str # 'retracement', 'extension', 'projection'
|
| 18 |
-
ratio: float
|
| 19 |
-
distance_from_current: float
|
| 20 |
-
strength: float
|
| 21 |
-
|
| 22 |
-
@dataclass
|
| 23 |
-
class SwingPoint:
|
| 24 |
-
"""Estrutura para pontos de swing"""
|
| 25 |
-
price: float
|
| 26 |
-
timestamp: str
|
| 27 |
-
type: str # 'high' ou 'low'
|
| 28 |
-
strength: float
|
| 29 |
-
|
| 30 |
-
@dataclass
|
| 31 |
-
class ConfluenceZone:
|
| 32 |
-
"""Estrutura para zonas de confluência"""
|
| 33 |
-
price_range: Tuple[float, float]
|
| 34 |
-
levels_count: int
|
| 35 |
-
strength: float
|
| 36 |
-
types: List[str]
|
| 37 |
-
|
| 38 |
-
@dataclass
|
| 39 |
-
class HarmonicPattern:
|
| 40 |
-
"""Estrutura para padrões harmônicos"""
|
| 41 |
-
name: str
|
| 42 |
-
completion_point: float
|
| 43 |
-
confidence: float
|
| 44 |
-
target_levels: List[float]
|
| 45 |
-
stop_loss: float
|
| 46 |
-
|
| 47 |
-
@dataclass
|
| 48 |
-
class AdvancedFibonacciAnalysis:
|
| 49 |
-
"""Análise avançada de Fibonacci completa"""
|
| 50 |
-
swing_high: float
|
| 51 |
-
swing_low: float
|
| 52 |
-
current_price: float
|
| 53 |
-
swing_range: float
|
| 54 |
-
retracement_levels: List[FibonacciLevel]
|
| 55 |
-
extension_levels: List[FibonacciLevel]
|
| 56 |
-
projection_levels: List[FibonacciLevel]
|
| 57 |
-
confluence_zones: List[ConfluenceZone]
|
| 58 |
-
harmonic_patterns: List[HarmonicPattern]
|
| 59 |
-
key_support: float
|
| 60 |
-
key_resistance: float
|
| 61 |
-
trend_direction: str
|
| 62 |
-
fibonacci_zone: str
|
| 63 |
-
overall_strength: float
|
| 64 |
-
trading_signal: str
|
| 65 |
-
alerts_count: int
|
| 66 |
-
|
| 67 |
-
class AdvancedFibonacciEngine:
|
| 68 |
-
"""Engine para análise avançada de Fibonacci"""
|
| 69 |
-
|
| 70 |
-
def __init__(self):
|
| 71 |
-
# Ratios de Fibonacci padrão
|
| 72 |
-
self.retracement_ratios = [0.236, 0.382, 0.5, 0.618, 0.786]
|
| 73 |
-
self.extension_ratios = [1.272, 1.414, 1.618, 2.0, 2.618]
|
| 74 |
-
self.projection_ratios = [0.618, 1.0, 1.272, 1.618]
|
| 75 |
-
|
| 76 |
-
# Configurações de confluência
|
| 77 |
-
self.confluence_threshold = 0.001 # 0.1% de tolerância
|
| 78 |
-
self.min_confluence_levels = 2
|
| 79 |
-
|
| 80 |
-
# Padrões harmônicos
|
| 81 |
-
self.harmonic_patterns = {
|
| 82 |
-
'Gartley': {'XA': 0.618, 'AB': 0.618, 'BC': 0.786, 'CD': 1.272},
|
| 83 |
-
'Butterfly': {'XA': 0.786, 'AB': 0.618, 'BC': 0.886, 'CD': 1.618},
|
| 84 |
-
'Bat': {'XA': 0.382, 'AB': 0.618, 'BC': 0.886, 'CD': 2.618},
|
| 85 |
-
'Crab': {'XA': 0.618, 'AB': 0.618, 'BC': 0.886, 'CD': 3.618}
|
| 86 |
-
}
|
| 87 |
-
|
| 88 |
-
def analyze_from_bot_data(self, bot_analysis: BotAnalysis) -> AdvancedFibonacciAnalysis:
|
| 89 |
-
"""Analisa dados do bot externo"""
|
| 90 |
-
fib_data = bot_analysis.fibonacci_analysis
|
| 91 |
-
market_data = bot_analysis.market_data
|
| 92 |
-
|
| 93 |
-
return self.perform_advanced_analysis(
|
| 94 |
-
swing_high=fib_data.swing_high,
|
| 95 |
-
swing_low=fib_data.swing_low,
|
| 96 |
-
current_price=fib_data.current_price,
|
| 97 |
-
historical_data=None, # Pode ser expandido futuramente
|
| 98 |
-
bot_fibonacci_data=fib_data
|
| 99 |
-
)
|
| 100 |
-
|
| 101 |
-
def perform_advanced_analysis(
|
| 102 |
-
self,
|
| 103 |
-
swing_high: float,
|
| 104 |
-
swing_low: float,
|
| 105 |
-
current_price: float,
|
| 106 |
-
historical_data: Optional[pd.DataFrame] = None,
|
| 107 |
-
bot_fibonacci_data: Optional[FibonacciAnalysis] = None
|
| 108 |
-
) -> AdvancedFibonacciAnalysis:
|
| 109 |
-
"""Realiza análise avançada de Fibonacci"""
|
| 110 |
-
|
| 111 |
-
try:
|
| 112 |
-
swing_range = swing_high - swing_low
|
| 113 |
-
|
| 114 |
-
# Calcular níveis de retracement
|
| 115 |
-
retracement_levels = self._calculate_retracement_levels(
|
| 116 |
-
swing_high, swing_low, current_price
|
| 117 |
-
)
|
| 118 |
-
|
| 119 |
-
# Calcular níveis de extensão
|
| 120 |
-
extension_levels = self._calculate_extension_levels(
|
| 121 |
-
swing_high, swing_low, current_price
|
| 122 |
-
)
|
| 123 |
-
|
| 124 |
-
# Calcular níveis de projeção
|
| 125 |
-
projection_levels = self._calculate_projection_levels(
|
| 126 |
-
swing_high, swing_low, current_price
|
| 127 |
-
)
|
| 128 |
-
|
| 129 |
-
# Identificar zonas de confluência
|
| 130 |
-
all_levels = retracement_levels + extension_levels + projection_levels
|
| 131 |
-
confluence_zones = self._identify_confluence_zones(all_levels)
|
| 132 |
-
|
| 133 |
-
# Detectar padrões harmônicos
|
| 134 |
-
harmonic_patterns = self._detect_harmonic_patterns(
|
| 135 |
-
swing_high, swing_low, current_price, historical_data
|
| 136 |
-
)
|
| 137 |
-
|
| 138 |
-
# Determinar suporte e resistência chave
|
| 139 |
-
key_support, key_resistance = self._determine_key_levels(
|
| 140 |
-
all_levels, current_price
|
| 141 |
-
)
|
| 142 |
-
|
| 143 |
-
# Determinar direção da tendência
|
| 144 |
-
trend_direction = self._determine_trend_direction(
|
| 145 |
-
swing_high, swing_low, current_price
|
| 146 |
-
)
|
| 147 |
-
|
| 148 |
-
# Determinar zona de Fibonacci
|
| 149 |
-
fibonacci_zone = self._determine_fibonacci_zone(
|
| 150 |
-
swing_high, swing_low, current_price
|
| 151 |
-
)
|
| 152 |
-
|
| 153 |
-
# Calcular força geral da análise
|
| 154 |
-
overall_strength = self._calculate_overall_strength(
|
| 155 |
-
retracement_levels, extension_levels, confluence_zones, harmonic_patterns
|
| 156 |
-
)
|
| 157 |
-
|
| 158 |
-
# Gerar sinal de trading
|
| 159 |
-
trading_signal = self._generate_trading_signal(
|
| 160 |
-
current_price, key_support, key_resistance, trend_direction, overall_strength
|
| 161 |
-
)
|
| 162 |
-
|
| 163 |
-
# Contar alertas (baseado nos dados do bot se disponível)
|
| 164 |
-
alerts_count = bot_fibonacci_data.alerts if bot_fibonacci_data else len(confluence_zones)
|
| 165 |
-
|
| 166 |
-
return AdvancedFibonacciAnalysis(
|
| 167 |
-
swing_high=swing_high,
|
| 168 |
-
swing_low=swing_low,
|
| 169 |
-
current_price=current_price,
|
| 170 |
-
swing_range=swing_range,
|
| 171 |
-
retracement_levels=retracement_levels,
|
| 172 |
-
extension_levels=extension_levels,
|
| 173 |
-
projection_levels=projection_levels,
|
| 174 |
-
confluence_zones=confluence_zones,
|
| 175 |
-
harmonic_patterns=harmonic_patterns,
|
| 176 |
-
key_support=key_support,
|
| 177 |
-
key_resistance=key_resistance,
|
| 178 |
-
trend_direction=trend_direction,
|
| 179 |
-
fibonacci_zone=fibonacci_zone,
|
| 180 |
-
overall_strength=overall_strength,
|
| 181 |
-
trading_signal=trading_signal,
|
| 182 |
-
alerts_count=alerts_count
|
| 183 |
-
)
|
| 184 |
-
|
| 185 |
-
except Exception as e:
|
| 186 |
-
logger.error(f"Erro na análise avançada de Fibonacci: {e}")
|
| 187 |
-
raise
|
| 188 |
-
|
| 189 |
-
def _calculate_retracement_levels(
|
| 190 |
-
self, swing_high: float, swing_low: float, current_price: float
|
| 191 |
-
) -> List[FibonacciLevel]:
|
| 192 |
-
"""Calcula níveis de retracement de Fibonacci"""
|
| 193 |
-
levels = []
|
| 194 |
-
swing_range = swing_high - swing_low
|
| 195 |
-
|
| 196 |
-
for ratio in self.retracement_ratios:
|
| 197 |
-
price = swing_high - (swing_range * ratio)
|
| 198 |
-
distance = abs(current_price - price)
|
| 199 |
-
strength = self._calculate_level_strength(price, current_price, 'retracement')
|
| 200 |
-
|
| 201 |
-
levels.append(FibonacciLevel(
|
| 202 |
-
level=ratio,
|
| 203 |
-
price=price,
|
| 204 |
-
type='retracement',
|
| 205 |
-
ratio=ratio,
|
| 206 |
-
distance_from_current=distance,
|
| 207 |
-
strength=strength
|
| 208 |
-
))
|
| 209 |
-
|
| 210 |
-
return sorted(levels, key=lambda x: x.distance_from_current)
|
| 211 |
-
|
| 212 |
-
def _calculate_extension_levels(
|
| 213 |
-
self, swing_high: float, swing_low: float, current_price: float
|
| 214 |
-
) -> List[FibonacciLevel]:
|
| 215 |
-
"""Calcula níveis de extensão de Fibonacci"""
|
| 216 |
-
levels = []
|
| 217 |
-
swing_range = swing_high - swing_low
|
| 218 |
-
|
| 219 |
-
for ratio in self.extension_ratios:
|
| 220 |
-
# Extensão para cima
|
| 221 |
-
price_up = swing_high + (swing_range * (ratio - 1))
|
| 222 |
-
distance_up = abs(current_price - price_up)
|
| 223 |
-
strength_up = self._calculate_level_strength(price_up, current_price, 'extension')
|
| 224 |
-
|
| 225 |
-
levels.append(FibonacciLevel(
|
| 226 |
-
level=ratio,
|
| 227 |
-
price=price_up,
|
| 228 |
-
type='extension_up',
|
| 229 |
-
ratio=ratio,
|
| 230 |
-
distance_from_current=distance_up,
|
| 231 |
-
strength=strength_up
|
| 232 |
-
))
|
| 233 |
-
|
| 234 |
-
# Extensão para baixo
|
| 235 |
-
price_down = swing_low - (swing_range * (ratio - 1))
|
| 236 |
-
distance_down = abs(current_price - price_down)
|
| 237 |
-
strength_down = self._calculate_level_strength(price_down, current_price, 'extension')
|
| 238 |
-
|
| 239 |
-
levels.append(FibonacciLevel(
|
| 240 |
-
level=ratio,
|
| 241 |
-
price=price_down,
|
| 242 |
-
type='extension_down',
|
| 243 |
-
ratio=ratio,
|
| 244 |
-
distance_from_current=distance_down,
|
| 245 |
-
strength=strength_down
|
| 246 |
-
))
|
| 247 |
-
|
| 248 |
-
return sorted(levels, key=lambda x: x.distance_from_current)
|
| 249 |
-
|
| 250 |
-
def _calculate_projection_levels(
|
| 251 |
-
self, swing_high: float, swing_low: float, current_price: float
|
| 252 |
-
) -> List[FibonacciLevel]:
|
| 253 |
-
"""Calcula níveis de projeção de Fibonacci"""
|
| 254 |
-
levels = []
|
| 255 |
-
swing_range = swing_high - swing_low
|
| 256 |
-
|
| 257 |
-
for ratio in self.projection_ratios:
|
| 258 |
-
# Projeção baseada no movimento atual
|
| 259 |
-
if current_price > (swing_high + swing_low) / 2: # Tendência de alta
|
| 260 |
-
price = current_price + (swing_range * ratio)
|
| 261 |
-
direction = 'projection_up'
|
| 262 |
-
else: # Tendência de baixa
|
| 263 |
-
price = current_price - (swing_range * ratio)
|
| 264 |
-
direction = 'projection_down'
|
| 265 |
-
|
| 266 |
-
distance = abs(current_price - price)
|
| 267 |
-
strength = self._calculate_level_strength(price, current_price, 'projection')
|
| 268 |
-
|
| 269 |
-
levels.append(FibonacciLevel(
|
| 270 |
-
level=ratio,
|
| 271 |
-
price=price,
|
| 272 |
-
type=direction,
|
| 273 |
-
ratio=ratio,
|
| 274 |
-
distance_from_current=distance,
|
| 275 |
-
strength=strength
|
| 276 |
-
))
|
| 277 |
-
|
| 278 |
-
return sorted(levels, key=lambda x: x.distance_from_current)
|
| 279 |
-
|
| 280 |
-
def _calculate_level_strength(
|
| 281 |
-
self, level_price: float, current_price: float, level_type: str
|
| 282 |
-
) -> float:
|
| 283 |
-
"""Calcula a força de um nível de Fibonacci"""
|
| 284 |
-
distance_factor = 1 / (1 + abs(level_price - current_price) / current_price)
|
| 285 |
-
|
| 286 |
-
type_weights = {
|
| 287 |
-
'retracement': 1.0,
|
| 288 |
-
'extension': 0.8,
|
| 289 |
-
'projection': 0.6
|
| 290 |
-
}
|
| 291 |
-
|
| 292 |
-
base_strength = type_weights.get(level_type, 0.5)
|
| 293 |
-
return base_strength * distance_factor
|
| 294 |
-
|
| 295 |
-
def _identify_confluence_zones(
|
| 296 |
-
self, all_levels: List[FibonacciLevel]
|
| 297 |
-
) -> List[ConfluenceZone]:
|
| 298 |
-
"""Identifica zonas de confluência entre níveis"""
|
| 299 |
-
confluence_zones = []
|
| 300 |
-
|
| 301 |
-
# Agrupar níveis próximos
|
| 302 |
-
sorted_levels = sorted(all_levels, key=lambda x: x.price)
|
| 303 |
-
|
| 304 |
-
i = 0
|
| 305 |
-
while i < len(sorted_levels):
|
| 306 |
-
current_level = sorted_levels[i]
|
| 307 |
-
zone_levels = [current_level]
|
| 308 |
-
zone_types = [current_level.type]
|
| 309 |
-
|
| 310 |
-
# Procurar níveis próximos
|
| 311 |
-
j = i + 1
|
| 312 |
-
while j < len(sorted_levels):
|
| 313 |
-
next_level = sorted_levels[j]
|
| 314 |
-
price_diff = abs(next_level.price - current_level.price) / current_level.price
|
| 315 |
-
|
| 316 |
-
if price_diff <= self.confluence_threshold:
|
| 317 |
-
zone_levels.append(next_level)
|
| 318 |
-
zone_types.append(next_level.type)
|
| 319 |
-
j += 1
|
| 320 |
-
else:
|
| 321 |
-
break
|
| 322 |
-
|
| 323 |
-
# Criar zona de confluência se houver níveis suficientes
|
| 324 |
-
if len(zone_levels) >= self.min_confluence_levels:
|
| 325 |
-
min_price = min(level.price for level in zone_levels)
|
| 326 |
-
max_price = max(level.price for level in zone_levels)
|
| 327 |
-
avg_strength = sum(level.strength for level in zone_levels) / len(zone_levels)
|
| 328 |
-
|
| 329 |
-
confluence_zones.append(ConfluenceZone(
|
| 330 |
-
price_range=(min_price, max_price),
|
| 331 |
-
levels_count=len(zone_levels),
|
| 332 |
-
strength=avg_strength * len(zone_levels), # Força multiplicada pelo número de níveis
|
| 333 |
-
types=list(set(zone_types))
|
| 334 |
-
))
|
| 335 |
-
|
| 336 |
-
i = j if j > i + 1 else i + 1
|
| 337 |
-
|
| 338 |
-
return sorted(confluence_zones, key=lambda x: x.strength, reverse=True)
|
| 339 |
-
|
| 340 |
-
def _detect_harmonic_patterns(
|
| 341 |
-
self, swing_high: float, swing_low: float, current_price: float,
|
| 342 |
-
historical_data: Optional[pd.DataFrame] = None
|
| 343 |
-
) -> List[HarmonicPattern]:
|
| 344 |
-
"""Detecta padrões harmônicos (implementação básica)"""
|
| 345 |
-
patterns = []
|
| 346 |
-
|
| 347 |
-
# Implementação simplificada - pode ser expandida com dados históricos
|
| 348 |
-
swing_range = swing_high - swing_low
|
| 349 |
-
|
| 350 |
-
for pattern_name, ratios in self.harmonic_patterns.items():
|
| 351 |
-
# Verificar se o preço atual está em uma posição válida para o padrão
|
| 352 |
-
completion_point = swing_low + (swing_range * ratios['CD'])
|
| 353 |
-
|
| 354 |
-
if abs(current_price - completion_point) / current_price < 0.02: # 2% de tolerância
|
| 355 |
-
confidence = 0.7 # Confiança básica
|
| 356 |
-
|
| 357 |
-
# Calcular alvos baseados no padrão
|
| 358 |
-
target_levels = [
|
| 359 |
-
completion_point + (swing_range * 0.382),
|
| 360 |
-
completion_point + (swing_range * 0.618),
|
| 361 |
-
completion_point + (swing_range * 1.0)
|
| 362 |
-
]
|
| 363 |
-
|
| 364 |
-
stop_loss = completion_point - (swing_range * 0.236)
|
| 365 |
-
|
| 366 |
-
patterns.append(HarmonicPattern(
|
| 367 |
-
name=pattern_name,
|
| 368 |
-
completion_point=completion_point,
|
| 369 |
-
confidence=confidence,
|
| 370 |
-
target_levels=target_levels,
|
| 371 |
-
stop_loss=stop_loss
|
| 372 |
-
))
|
| 373 |
-
|
| 374 |
-
return sorted(patterns, key=lambda x: x.confidence, reverse=True)
|
| 375 |
-
|
| 376 |
-
def _determine_key_levels(
|
| 377 |
-
self, all_levels: List[FibonacciLevel], current_price: float
|
| 378 |
-
) -> Tuple[float, float]:
|
| 379 |
-
"""Determina níveis chave de suporte e resistência"""
|
| 380 |
-
support_levels = [level for level in all_levels if level.price < current_price]
|
| 381 |
-
resistance_levels = [level for level in all_levels if level.price > current_price]
|
| 382 |
-
|
| 383 |
-
# Suporte mais próximo e forte
|
| 384 |
-
key_support = current_price
|
| 385 |
-
if support_levels:
|
| 386 |
-
key_support = max(support_levels, key=lambda x: x.strength).price
|
| 387 |
-
|
| 388 |
-
# Resistência mais próxima e forte
|
| 389 |
-
key_resistance = current_price
|
| 390 |
-
if resistance_levels:
|
| 391 |
-
key_resistance = min(resistance_levels, key=lambda x: x.strength).price
|
| 392 |
-
|
| 393 |
-
return key_support, key_resistance
|
| 394 |
-
|
| 395 |
-
def _determine_trend_direction(
|
| 396 |
-
self, swing_high: float, swing_low: float, current_price: float
|
| 397 |
-
) -> str:
|
| 398 |
-
"""Determina direção da tendência"""
|
| 399 |
-
mid_point = (swing_high + swing_low) / 2
|
| 400 |
-
|
| 401 |
-
if current_price > mid_point + (swing_high - swing_low) * 0.1:
|
| 402 |
-
return 'ALTA'
|
| 403 |
-
elif current_price < mid_point - (swing_high - swing_low) * 0.1:
|
| 404 |
-
return 'BAIXA'
|
| 405 |
-
else:
|
| 406 |
-
return 'LATERAL'
|
| 407 |
-
|
| 408 |
-
def _determine_fibonacci_zone(
|
| 409 |
-
self, swing_high: float, swing_low: float, current_price: float
|
| 410 |
-
) -> str:
|
| 411 |
-
"""Determina zona de Fibonacci atual"""
|
| 412 |
-
swing_range = swing_high - swing_low
|
| 413 |
-
position = (current_price - swing_low) / swing_range
|
| 414 |
-
|
| 415 |
-
if position >= 0.786:
|
| 416 |
-
return 'ZONA_ALTA'
|
| 417 |
-
elif position >= 0.618:
|
| 418 |
-
return 'ZONA_MEDIA_ALTA'
|
| 419 |
-
elif position >= 0.382:
|
| 420 |
-
return 'ZONA_MEDIA'
|
| 421 |
-
elif position >= 0.236:
|
| 422 |
-
return 'ZONA_MEDIA_BAIXA'
|
| 423 |
-
else:
|
| 424 |
-
return 'ZONA_BAIXA'
|
| 425 |
-
|
| 426 |
-
def _calculate_overall_strength(
|
| 427 |
-
self, retracement_levels: List[FibonacciLevel],
|
| 428 |
-
extension_levels: List[FibonacciLevel],
|
| 429 |
-
confluence_zones: List[ConfluenceZone],
|
| 430 |
-
harmonic_patterns: List[HarmonicPattern]
|
| 431 |
-
) -> float:
|
| 432 |
-
"""Calcula força geral da análise"""
|
| 433 |
-
# Força baseada em níveis próximos
|
| 434 |
-
level_strength = sum(level.strength for level in retracement_levels[:3]) # Top 3
|
| 435 |
-
level_strength += sum(level.strength for level in extension_levels[:3]) # Top 3
|
| 436 |
-
|
| 437 |
-
# Força das zonas de confluência
|
| 438 |
-
confluence_strength = sum(zone.strength for zone in confluence_zones)
|
| 439 |
-
|
| 440 |
-
# Força dos padrões harmônicos
|
| 441 |
-
harmonic_strength = sum(pattern.confidence for pattern in harmonic_patterns)
|
| 442 |
-
|
| 443 |
-
# Normalizar para 0-1
|
| 444 |
-
total_strength = (level_strength + confluence_strength + harmonic_strength) / 10
|
| 445 |
-
return min(total_strength, 1.0)
|
| 446 |
-
|
| 447 |
-
def _generate_trading_signal(
|
| 448 |
-
self, current_price: float, key_support: float, key_resistance: float,
|
| 449 |
-
trend_direction: str, overall_strength: float
|
| 450 |
-
) -> str:
|
| 451 |
-
"""Gera sinal de trading baseado na análise"""
|
| 452 |
-
support_distance = abs(current_price - key_support) / current_price
|
| 453 |
-
resistance_distance = abs(current_price - key_resistance) / current_price
|
| 454 |
-
|
| 455 |
-
if overall_strength < 0.3:
|
| 456 |
-
return 'HOLD'
|
| 457 |
-
|
| 458 |
-
if trend_direction == 'ALTA' and support_distance < 0.02:
|
| 459 |
-
return 'BUY'
|
| 460 |
-
elif trend_direction == 'BAIXA' and resistance_distance < 0.02:
|
| 461 |
-
return 'SELL'
|
| 462 |
-
elif support_distance < resistance_distance and overall_strength > 0.6:
|
| 463 |
-
return 'BUY'
|
| 464 |
-
elif resistance_distance < support_distance and overall_strength > 0.6:
|
| 465 |
-
return 'SELL'
|
| 466 |
-
else:
|
| 467 |
-
return 'HOLD'
|
| 468 |
-
|
| 469 |
-
def format_analysis_report(self, analysis: AdvancedFibonacciAnalysis) -> str:
|
| 470 |
-
"""Formata relatório da análise"""
|
| 471 |
-
report = f"""
|
| 472 |
-
🔮 ANÁLISE AVANÇADA DE FIBONACCI
|
| 473 |
-
{'='*50}
|
| 474 |
-
|
| 475 |
-
📊 DADOS BÁSICOS:
|
| 476 |
-
Swing Alto: {analysis.swing_high:,.2f}
|
| 477 |
-
Swing Baixo: {analysis.swing_low:,.2f}
|
| 478 |
-
Preço Atual: {analysis.current_price:,.2f}
|
| 479 |
-
Range: {analysis.swing_range:,.2f}
|
| 480 |
-
|
| 481 |
-
📈 NÍVEIS DE RETRACEMENT ({len(analysis.retracement_levels)}):
|
| 482 |
-
"""
|
| 483 |
-
|
| 484 |
-
for level in analysis.retracement_levels[:5]: # Top 5
|
| 485 |
-
report += f" {level.ratio:.1%}: {level.price:,.2f} (Força: {level.strength:.2f})\n"
|
| 486 |
-
|
| 487 |
-
report += f"\n📊 NÍVEIS DE EXTENSÃO ({len(analysis.extension_levels)}):\n"
|
| 488 |
-
for level in analysis.extension_levels[:5]: # Top 5
|
| 489 |
-
report += f" {level.ratio:.1%}: {level.price:,.2f} ({level.type})\n"
|
| 490 |
-
|
| 491 |
-
if analysis.confluence_zones:
|
| 492 |
-
report += f"\n🎯 ZONAS DE CONFLUÊNCIA ({len(analysis.confluence_zones)}):\n"
|
| 493 |
-
for zone in analysis.confluence_zones[:3]: # Top 3
|
| 494 |
-
report += f" {zone.price_range[0]:,.2f} - {zone.price_range[1]:,.2f} ({zone.levels_count} níveis)\n"
|
| 495 |
-
|
| 496 |
-
if analysis.harmonic_patterns:
|
| 497 |
-
report += f"\n🎼 PADRÕES HARMÔNICOS ({len(analysis.harmonic_patterns)}):\n"
|
| 498 |
-
for pattern in analysis.harmonic_patterns:
|
| 499 |
-
report += f" {pattern.name}: {pattern.confidence:.1%} confiança\n"
|
| 500 |
-
|
| 501 |
-
report += f"""
|
| 502 |
-
|
| 503 |
-
🎯 NÍVEIS CHAVE:
|
| 504 |
-
Suporte: {analysis.key_support:,.2f}
|
| 505 |
-
Resistência: {analysis.key_resistance:,.2f}
|
| 506 |
-
|
| 507 |
-
📊 ANÁLISE GERAL:
|
| 508 |
-
Tendência: {analysis.trend_direction}
|
| 509 |
-
Zona Fibonacci: {analysis.fibonacci_zone}
|
| 510 |
-
Força da Análise: {analysis.overall_strength:.1%}
|
| 511 |
-
Sinal: {analysis.trading_signal}
|
| 512 |
-
Alertas: {analysis.alerts_count}
|
| 513 |
-
"""
|
| 514 |
-
|
| 515 |
-
return report
|
| 516 |
-
|
| 517 |
-
# Exemplo de uso
|
| 518 |
-
if __name__ == "__main__":
|
| 519 |
-
engine = AdvancedFibonacciEngine()
|
| 520 |
-
|
| 521 |
-
# Exemplo com dados do bot
|
| 522 |
-
analysis = engine.perform_advanced_analysis(
|
| 523 |
-
swing_high=140570.0,
|
| 524 |
-
swing_low=139540.0,
|
| 525 |
-
current_price=140135.0
|
| 526 |
-
)
|
| 527 |
-
|
| 528 |
-
print(engine.format_analysis_report(analysis))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log_parser.py
DELETED
|
@@ -1,335 +0,0 @@
|
|
| 1 |
-
import re
|
| 2 |
-
import json
|
| 3 |
-
from datetime import datetime
|
| 4 |
-
from typing import Dict, List, Optional, Any
|
| 5 |
-
from dataclasses import dataclass, asdict
|
| 6 |
-
import logging
|
| 7 |
-
|
| 8 |
-
# Configurar logging
|
| 9 |
-
logging.basicConfig(level=logging.INFO)
|
| 10 |
-
logger = logging.getLogger(__name__)
|
| 11 |
-
|
| 12 |
-
@dataclass
|
| 13 |
-
class MarketData:
|
| 14 |
-
"""Estrutura para dados de mercado"""
|
| 15 |
-
symbol: str
|
| 16 |
-
current_price: float
|
| 17 |
-
variation: float
|
| 18 |
-
variation_percent: float
|
| 19 |
-
high: float
|
| 20 |
-
low: float
|
| 21 |
-
volume: int
|
| 22 |
-
timestamp: str
|
| 23 |
-
|
| 24 |
-
@dataclass
|
| 25 |
-
class TechnicalIndicators:
|
| 26 |
-
"""Estrutura para indicadores técnicos"""
|
| 27 |
-
rsi: float
|
| 28 |
-
rsi_status: str
|
| 29 |
-
ema_fast: float
|
| 30 |
-
ema_slow: float
|
| 31 |
-
ema_trend: str
|
| 32 |
-
bollinger_status: str
|
| 33 |
-
bollinger_upper: float
|
| 34 |
-
bollinger_lower: float
|
| 35 |
-
atr: float
|
| 36 |
-
volatility: str
|
| 37 |
-
volatility_multiplier: float
|
| 38 |
-
|
| 39 |
-
@dataclass
|
| 40 |
-
class FibonacciAnalysis:
|
| 41 |
-
"""Estrutura para análise de Fibonacci"""
|
| 42 |
-
swing_high: float
|
| 43 |
-
swing_low: float
|
| 44 |
-
current_price: float
|
| 45 |
-
swing_difference: float
|
| 46 |
-
retracement_levels: int
|
| 47 |
-
extension_levels: int
|
| 48 |
-
projection_levels: int
|
| 49 |
-
total_levels: int
|
| 50 |
-
confluence_zones: int
|
| 51 |
-
harmonic_patterns: int
|
| 52 |
-
temporal_projections: int
|
| 53 |
-
analysis_strength: float
|
| 54 |
-
zone: str
|
| 55 |
-
support: float
|
| 56 |
-
resistance: float
|
| 57 |
-
alerts: int
|
| 58 |
-
signal: str
|
| 59 |
-
|
| 60 |
-
@dataclass
|
| 61 |
-
class BotAnalysis:
|
| 62 |
-
"""Estrutura completa da análise do bot"""
|
| 63 |
-
analysis_number: int
|
| 64 |
-
timestamp: str
|
| 65 |
-
market_data: MarketData
|
| 66 |
-
technical_indicators: TechnicalIndicators
|
| 67 |
-
fibonacci_analysis: FibonacciAnalysis
|
| 68 |
-
performance_time: Optional[float] = None
|
| 69 |
-
|
| 70 |
-
class VampireBotLogParser:
|
| 71 |
-
"""Parser para logs do Vampire Trading Bot"""
|
| 72 |
-
|
| 73 |
-
def __init__(self):
|
| 74 |
-
self.patterns = self._compile_patterns()
|
| 75 |
-
|
| 76 |
-
def _compile_patterns(self) -> Dict[str, re.Pattern]:
|
| 77 |
-
"""Compila padrões regex para extração de dados"""
|
| 78 |
-
return {
|
| 79 |
-
'analysis_header': re.compile(r'⏰ Análise #(\d+) - ([\d:]+)'),
|
| 80 |
-
'market_symbol': re.compile(r'📊 DADOS DE MERCADO - (\w+)'),
|
| 81 |
-
'current_price': re.compile(r'Preço Atual: ([\d.]+) ↗'),
|
| 82 |
-
'variation': re.compile(r'Variação: ([+-][\d.]+) \(([+-][\d.]+)%\)'),
|
| 83 |
-
'high_low': re.compile(r'Máxima: ([\d.]+)\nMínima: ([\d.]+)'),
|
| 84 |
-
'volume': re.compile(r'Volume: (\d+)'),
|
| 85 |
-
'rsi': re.compile(r'RSI \(14\): ([\d.]+) \((\w+)\)'),
|
| 86 |
-
'ema': re.compile(r'EMA Rápida: ([\d.]+)\nEMA Lenta: ([\d.]+)'),
|
| 87 |
-
'ema_trend': re.compile(r'Tendência EMA: (\w+)'),
|
| 88 |
-
'bollinger': re.compile(r'Bollinger: ([\w\s]+)\n\s+Superior: ([\d.]+)\n\s+Inferior: ([\d.]+)'),
|
| 89 |
-
'atr': re.compile(r'ATR: ([\d.]+)'),
|
| 90 |
-
'volatility': re.compile(r'Volatilidade: (\w+) \(([\d.]+)x\)'),
|
| 91 |
-
'swing_points': re.compile(r'📊 Swing Points - Alta: ([\d,]+\.\d+), Baixa: ([\d,]+\.\d+), Atual: ([\d,]+\.\d+)'),
|
| 92 |
-
'swing_difference': re.compile(r'📏 Diferença Swing: ([\d,]+\.\d+) pontos'),
|
| 93 |
-
'retracement_levels': re.compile(r'📈 Níveis de Retracement calculados: (\d+) níveis'),
|
| 94 |
-
'extension_levels': re.compile(r'📊 Níveis de Extensão calculados: (\d+) níveis'),
|
| 95 |
-
'projection_levels': re.compile(r'🎯 Níveis de Projeção calculados: (\d+) níveis'),
|
| 96 |
-
'total_levels': re.compile(r'🔢 Total de níveis Fibonacci: (\d+)'),
|
| 97 |
-
'confluence_zones': re.compile(r'🎯 Zonas de Confluência detectadas: (\d+)'),
|
| 98 |
-
'harmonic_patterns': re.compile(r'🎼 Padrões Harmônicos detectados: (\d+)'),
|
| 99 |
-
'temporal_projections': re.compile(r'⏰ Projeções Temporais calculadas: (\d+)'),
|
| 100 |
-
'analysis_strength': re.compile(r'💪 Força Geral da Análise: ([\d.]+)'),
|
| 101 |
-
'fibonacci_conclusion': re.compile(r'🔮 ANÁLISE CONCLUÍDA - Zona: (\w+), Suporte: ([\d.]+), Resistência: ([\d.]+)'),
|
| 102 |
-
'performance_time': re.compile(r'Análise de mercado lenta: ([\d.]+)s'),
|
| 103 |
-
'fibonacci_signal': re.compile(r'🔮 Fibonacci Avançado:\s+Alertas:(\d+) FibSinal:(\w+)')
|
| 104 |
-
}
|
| 105 |
-
|
| 106 |
-
def parse_log_content(self, log_content: str) -> Optional[BotAnalysis]:
|
| 107 |
-
"""Parseia o conteúdo completo do log"""
|
| 108 |
-
try:
|
| 109 |
-
# Extrair cabeçalho da análise
|
| 110 |
-
analysis_match = self.patterns['analysis_header'].search(log_content)
|
| 111 |
-
if not analysis_match:
|
| 112 |
-
logger.warning("Cabeçalho da análise não encontrado")
|
| 113 |
-
return None
|
| 114 |
-
|
| 115 |
-
analysis_number = int(analysis_match.group(1))
|
| 116 |
-
timestamp = analysis_match.group(2)
|
| 117 |
-
|
| 118 |
-
# Extrair dados de mercado
|
| 119 |
-
market_data = self._extract_market_data(log_content)
|
| 120 |
-
if not market_data:
|
| 121 |
-
logger.warning("Dados de mercado não encontrados")
|
| 122 |
-
return None
|
| 123 |
-
|
| 124 |
-
# Extrair indicadores técnicos
|
| 125 |
-
technical_indicators = self._extract_technical_indicators(log_content)
|
| 126 |
-
if not technical_indicators:
|
| 127 |
-
logger.warning("Indicadores técnicos não encontrados")
|
| 128 |
-
return None
|
| 129 |
-
|
| 130 |
-
# Extrair análise de Fibonacci
|
| 131 |
-
fibonacci_analysis = self._extract_fibonacci_analysis(log_content)
|
| 132 |
-
if not fibonacci_analysis:
|
| 133 |
-
logger.warning("Análise de Fibonacci não encontrada")
|
| 134 |
-
return None
|
| 135 |
-
|
| 136 |
-
# Extrair tempo de performance (opcional)
|
| 137 |
-
performance_match = self.patterns['performance_time'].search(log_content)
|
| 138 |
-
performance_time = float(performance_match.group(1)) if performance_match else None
|
| 139 |
-
|
| 140 |
-
return BotAnalysis(
|
| 141 |
-
analysis_number=analysis_number,
|
| 142 |
-
timestamp=timestamp,
|
| 143 |
-
market_data=market_data,
|
| 144 |
-
technical_indicators=technical_indicators,
|
| 145 |
-
fibonacci_analysis=fibonacci_analysis,
|
| 146 |
-
performance_time=performance_time
|
| 147 |
-
)
|
| 148 |
-
|
| 149 |
-
except Exception as e:
|
| 150 |
-
logger.error(f"Erro ao parsear log: {e}")
|
| 151 |
-
return None
|
| 152 |
-
|
| 153 |
-
def _extract_market_data(self, content: str) -> Optional[MarketData]:
|
| 154 |
-
"""Extrai dados de mercado do log"""
|
| 155 |
-
try:
|
| 156 |
-
symbol_match = self.patterns['market_symbol'].search(content)
|
| 157 |
-
price_match = self.patterns['current_price'].search(content)
|
| 158 |
-
variation_match = self.patterns['variation'].search(content)
|
| 159 |
-
high_low_match = self.patterns['high_low'].search(content)
|
| 160 |
-
volume_match = self.patterns['volume'].search(content)
|
| 161 |
-
|
| 162 |
-
if not all([symbol_match, price_match, variation_match, high_low_match, volume_match]):
|
| 163 |
-
return None
|
| 164 |
-
|
| 165 |
-
return MarketData(
|
| 166 |
-
symbol=symbol_match.group(1),
|
| 167 |
-
current_price=float(price_match.group(1)),
|
| 168 |
-
variation=float(variation_match.group(1)),
|
| 169 |
-
variation_percent=float(variation_match.group(2)),
|
| 170 |
-
high=float(high_low_match.group(1)),
|
| 171 |
-
low=float(high_low_match.group(2)),
|
| 172 |
-
volume=int(volume_match.group(1)),
|
| 173 |
-
timestamp=datetime.now().isoformat()
|
| 174 |
-
)
|
| 175 |
-
|
| 176 |
-
except Exception as e:
|
| 177 |
-
logger.error(f"Erro ao extrair dados de mercado: {e}")
|
| 178 |
-
return None
|
| 179 |
-
|
| 180 |
-
def _extract_technical_indicators(self, content: str) -> Optional[TechnicalIndicators]:
|
| 181 |
-
"""Extrai indicadores técnicos do log"""
|
| 182 |
-
try:
|
| 183 |
-
rsi_match = self.patterns['rsi'].search(content)
|
| 184 |
-
ema_match = self.patterns['ema'].search(content)
|
| 185 |
-
ema_trend_match = self.patterns['ema_trend'].search(content)
|
| 186 |
-
bollinger_match = self.patterns['bollinger'].search(content)
|
| 187 |
-
atr_match = self.patterns['atr'].search(content)
|
| 188 |
-
volatility_match = self.patterns['volatility'].search(content)
|
| 189 |
-
|
| 190 |
-
if not all([rsi_match, ema_match, ema_trend_match, bollinger_match, atr_match, volatility_match]):
|
| 191 |
-
return None
|
| 192 |
-
|
| 193 |
-
return TechnicalIndicators(
|
| 194 |
-
rsi=float(rsi_match.group(1)),
|
| 195 |
-
rsi_status=rsi_match.group(2),
|
| 196 |
-
ema_fast=float(ema_match.group(1)),
|
| 197 |
-
ema_slow=float(ema_match.group(2)),
|
| 198 |
-
ema_trend=ema_trend_match.group(1),
|
| 199 |
-
bollinger_status=bollinger_match.group(1).strip(),
|
| 200 |
-
bollinger_upper=float(bollinger_match.group(2)),
|
| 201 |
-
bollinger_lower=float(bollinger_match.group(3)),
|
| 202 |
-
atr=float(atr_match.group(1)),
|
| 203 |
-
volatility=volatility_match.group(1),
|
| 204 |
-
volatility_multiplier=float(volatility_match.group(2))
|
| 205 |
-
)
|
| 206 |
-
|
| 207 |
-
except Exception as e:
|
| 208 |
-
logger.error(f"Erro ao extrair indicadores técnicos: {e}")
|
| 209 |
-
return None
|
| 210 |
-
|
| 211 |
-
def _extract_fibonacci_analysis(self, content: str) -> Optional[FibonacciAnalysis]:
|
| 212 |
-
"""Extrai análise de Fibonacci do log"""
|
| 213 |
-
try:
|
| 214 |
-
# Buscar pelos últimos valores (análise final)
|
| 215 |
-
swing_matches = list(self.patterns['swing_points'].finditer(content))
|
| 216 |
-
swing_diff_matches = list(self.patterns['swing_difference'].finditer(content))
|
| 217 |
-
retracement_matches = list(self.patterns['retracement_levels'].finditer(content))
|
| 218 |
-
extension_matches = list(self.patterns['extension_levels'].finditer(content))
|
| 219 |
-
projection_matches = list(self.patterns['projection_levels'].finditer(content))
|
| 220 |
-
total_matches = list(self.patterns['total_levels'].finditer(content))
|
| 221 |
-
confluence_matches = list(self.patterns['confluence_zones'].finditer(content))
|
| 222 |
-
harmonic_matches = list(self.patterns['harmonic_patterns'].finditer(content))
|
| 223 |
-
temporal_matches = list(self.patterns['temporal_projections'].finditer(content))
|
| 224 |
-
strength_matches = list(self.patterns['analysis_strength'].finditer(content))
|
| 225 |
-
conclusion_matches = list(self.patterns['fibonacci_conclusion'].finditer(content))
|
| 226 |
-
signal_match = self.patterns['fibonacci_signal'].search(content)
|
| 227 |
-
|
| 228 |
-
if not (swing_matches and conclusion_matches and signal_match):
|
| 229 |
-
return None
|
| 230 |
-
|
| 231 |
-
# Usar os últimos valores encontrados
|
| 232 |
-
swing_match = swing_matches[-1]
|
| 233 |
-
conclusion_match = conclusion_matches[-1]
|
| 234 |
-
|
| 235 |
-
swing_high = float(swing_match.group(1).replace(',', ''))
|
| 236 |
-
swing_low = float(swing_match.group(2).replace(',', ''))
|
| 237 |
-
current_price = float(swing_match.group(3).replace(',', ''))
|
| 238 |
-
|
| 239 |
-
return FibonacciAnalysis(
|
| 240 |
-
swing_high=swing_high,
|
| 241 |
-
swing_low=swing_low,
|
| 242 |
-
current_price=current_price,
|
| 243 |
-
swing_difference=float(swing_diff_matches[-1].group(1).replace(',', '')) if swing_diff_matches else 0,
|
| 244 |
-
retracement_levels=int(retracement_matches[-1].group(1)) if retracement_matches else 0,
|
| 245 |
-
extension_levels=int(extension_matches[-1].group(1)) if extension_matches else 0,
|
| 246 |
-
projection_levels=int(projection_matches[-1].group(1)) if projection_matches else 0,
|
| 247 |
-
total_levels=int(total_matches[-1].group(1)) if total_matches else 0,
|
| 248 |
-
confluence_zones=int(confluence_matches[-1].group(1)) if confluence_matches else 0,
|
| 249 |
-
harmonic_patterns=int(harmonic_matches[-1].group(1)) if harmonic_matches else 0,
|
| 250 |
-
temporal_projections=int(temporal_matches[-1].group(1)) if temporal_matches else 0,
|
| 251 |
-
analysis_strength=float(strength_matches[-1].group(1)) if strength_matches else 0.0,
|
| 252 |
-
zone=conclusion_match.group(1),
|
| 253 |
-
support=float(conclusion_match.group(2)),
|
| 254 |
-
resistance=float(conclusion_match.group(3)),
|
| 255 |
-
alerts=int(signal_match.group(1)),
|
| 256 |
-
signal=signal_match.group(2)
|
| 257 |
-
)
|
| 258 |
-
|
| 259 |
-
except Exception as e:
|
| 260 |
-
logger.error(f"Erro ao extrair análise de Fibonacci: {e}")
|
| 261 |
-
return None
|
| 262 |
-
|
| 263 |
-
def parse_log_file(self, file_path: str) -> Optional[BotAnalysis]:
|
| 264 |
-
"""Parseia arquivo de log"""
|
| 265 |
-
try:
|
| 266 |
-
with open(file_path, 'r', encoding='utf-8') as file:
|
| 267 |
-
content = file.read()
|
| 268 |
-
return self.parse_log_content(content)
|
| 269 |
-
except Exception as e:
|
| 270 |
-
logger.error(f"Erro ao ler arquivo de log: {e}")
|
| 271 |
-
return None
|
| 272 |
-
|
| 273 |
-
def to_dict(self, analysis: BotAnalysis) -> Dict[str, Any]:
|
| 274 |
-
"""Converte análise para dicionário"""
|
| 275 |
-
return asdict(analysis)
|
| 276 |
-
|
| 277 |
-
def to_json(self, analysis: BotAnalysis) -> str:
|
| 278 |
-
"""Converte análise para JSON"""
|
| 279 |
-
return json.dumps(self.to_dict(analysis), indent=2, ensure_ascii=False)
|
| 280 |
-
|
| 281 |
-
# Exemplo de uso
|
| 282 |
-
if __name__ == "__main__":
|
| 283 |
-
parser = VampireBotLogParser()
|
| 284 |
-
|
| 285 |
-
# Exemplo com o log fornecido
|
| 286 |
-
sample_log = """
|
| 287 |
-
⏰ Análise #8 - 09:46:58
|
| 288 |
-
|
| 289 |
-
================================================================================
|
| 290 |
-
🧛 VAMPIRE TRADING BOT - ANÁLISE DETALHADA
|
| 291 |
-
================================================================================
|
| 292 |
-
|
| 293 |
-
📊 DADOS DE MERCADO - WINV25
|
| 294 |
-
──────────────────────────────────────────────────
|
| 295 |
-
Preço Atual: 140135.00000 ↗
|
| 296 |
-
Variação: +5.00000 (+0.00%)
|
| 297 |
-
Máxima: 140155.00000
|
| 298 |
-
Mínima: 140075.00000
|
| 299 |
-
Volume: 5023
|
| 300 |
-
|
| 301 |
-
📈 INDICADORES TÉCNICOS
|
| 302 |
-
──────────────────────────────────────────────────
|
| 303 |
-
RSI (14): 46.39 (NEUTRO)
|
| 304 |
-
EMA Rápida: 140192.30752
|
| 305 |
-
EMA Lenta: 140221.86717
|
| 306 |
-
Tendência EMA: BAIXA
|
| 307 |
-
Bollinger: DENTRO DAS BANDAS
|
| 308 |
-
Superior: 140672.37317
|
| 309 |
-
Inferior: 139913.62683
|
| 310 |
-
ATR: 170.73782
|
| 311 |
-
Volatilidade: MÉDIA (1.23x)
|
| 312 |
-
🔮 Fibonacci Avançado: Alertas:15 FibSinal:HOLD
|
| 313 |
-
"""
|
| 314 |
-
|
| 315 |
-
# Simular dados de Fibonacci (já que não estão completos no exemplo)
|
| 316 |
-
full_sample = sample_log + """
|
| 317 |
-
2025-08-27 09:46:58,333 - src.core.analysis.advanced_fibonacci - INFO - 🔮 ANÁLISE CONCLUÍDA - Zona: ZONA_MEDIA_ALTA, Suporte: 140133.28, Resistência: 140176.54
|
| 318 |
-
2025-08-27 09:46:58,218 - src.core.analysis.advanced_fibonacci - INFO - 📊 Swing Points - Alta: 140,570.00, Baixa: 139,540.00, Atual: 140,135.00
|
| 319 |
-
2025-08-27 09:46:58,219 - src.core.analysis.advanced_fibonacci - INFO - 📏 Diferença Swing: 1,030.00 pontos
|
| 320 |
-
2025-08-27 09:46:58,244 - src.core.analysis.advanced_fibonacci - INFO - 📈 Níveis de Retracement calculados: 13 níveis
|
| 321 |
-
2025-08-27 09:46:58,297 - src.core.analysis.advanced_fibonacci - INFO - 📊 Níveis de Extensão calculados: 11 níveis
|
| 322 |
-
2025-08-27 09:46:58,323 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Níveis de Projeção calculados: 8 níveis
|
| 323 |
-
2025-08-27 09:46:58,325 - src.core.analysis.advanced_fibonacci - INFO - 🔢 Total de níveis Fibonacci: 32
|
| 324 |
-
2025-08-27 09:46:58,327 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Zonas de Confluência detectadas: 0
|
| 325 |
-
2025-08-27 09:46:58,329 - src.core.analysis.advanced_fibonacci - INFO - 🎼 Padrões Harmônicos detectados: 0
|
| 326 |
-
2025-08-27 09:46:58,332 - src.core.analysis.advanced_fibonacci - INFO - ⏰ Projeções Temporais calculadas: 0
|
| 327 |
-
2025-08-27 09:46:58,332 - src.core.analysis.advanced_fibonacci - INFO - 💪 Força Geral da Análise: 0.00
|
| 328 |
-
"""
|
| 329 |
-
|
| 330 |
-
result = parser.parse_log_content(full_sample)
|
| 331 |
-
if result:
|
| 332 |
-
print("✅ Log parseado com sucesso!")
|
| 333 |
-
print(parser.to_json(result))
|
| 334 |
-
else:
|
| 335 |
-
print("❌ Erro ao parsear log")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
real_time_integration.py
DELETED
|
@@ -1,358 +0,0 @@
|
|
| 1 |
-
import asyncio
|
| 2 |
-
import os
|
| 3 |
-
import time
|
| 4 |
-
from datetime import datetime
|
| 5 |
-
from typing import Dict, Any, Optional, Callable
|
| 6 |
-
from pathlib import Path
|
| 7 |
-
import json
|
| 8 |
-
import logging
|
| 9 |
-
from dataclasses import dataclass, asdict
|
| 10 |
-
from threading import Thread, Event
|
| 11 |
-
import queue
|
| 12 |
-
|
| 13 |
-
from log_parser import VampireBotLogParser, BotAnalysis
|
| 14 |
-
from market_analysis import TechnicalAnalysisEngine
|
| 15 |
-
|
| 16 |
-
@dataclass
|
| 17 |
-
class RealTimeConfig:
|
| 18 |
-
"""Configuração para integração em tempo real"""
|
| 19 |
-
log_file_path: str
|
| 20 |
-
check_interval: float = 1.0 # segundos
|
| 21 |
-
max_queue_size: int = 100
|
| 22 |
-
enable_notifications: bool = True
|
| 23 |
-
auto_analysis: bool = True
|
| 24 |
-
backup_logs: bool = True
|
| 25 |
-
|
| 26 |
-
@dataclass
|
| 27 |
-
class BotEvent:
|
| 28 |
-
"""Evento do bot em tempo real"""
|
| 29 |
-
timestamp: datetime
|
| 30 |
-
event_type: str # 'new_analysis', 'fibonacci_alert', 'signal_change'
|
| 31 |
-
data: Dict[str, Any]
|
| 32 |
-
priority: str = 'normal' # 'low', 'normal', 'high', 'critical'
|
| 33 |
-
|
| 34 |
-
class FileWatcher:
|
| 35 |
-
"""Monitor de arquivos para detectar mudanças em tempo real"""
|
| 36 |
-
|
| 37 |
-
def __init__(self, file_path: str, callback: Callable[[str], None]):
|
| 38 |
-
self.file_path = Path(file_path)
|
| 39 |
-
self.callback = callback
|
| 40 |
-
self.last_modified = 0
|
| 41 |
-
self.last_size = 0
|
| 42 |
-
self.running = False
|
| 43 |
-
self._stop_event = Event()
|
| 44 |
-
|
| 45 |
-
def start(self):
|
| 46 |
-
"""Inicia o monitoramento do arquivo"""
|
| 47 |
-
self.running = True
|
| 48 |
-
self._stop_event.clear()
|
| 49 |
-
|
| 50 |
-
if self.file_path.exists():
|
| 51 |
-
stat = self.file_path.stat()
|
| 52 |
-
self.last_modified = stat.st_mtime
|
| 53 |
-
self.last_size = stat.st_size
|
| 54 |
-
|
| 55 |
-
def stop(self):
|
| 56 |
-
"""Para o monitoramento"""
|
| 57 |
-
self.running = False
|
| 58 |
-
self._stop_event.set()
|
| 59 |
-
|
| 60 |
-
def check_changes(self) -> bool:
|
| 61 |
-
"""Verifica se o arquivo foi modificado"""
|
| 62 |
-
if not self.file_path.exists():
|
| 63 |
-
return False
|
| 64 |
-
|
| 65 |
-
try:
|
| 66 |
-
stat = self.file_path.stat()
|
| 67 |
-
current_modified = stat.st_mtime
|
| 68 |
-
current_size = stat.st_size
|
| 69 |
-
|
| 70 |
-
# Verifica se houve mudança
|
| 71 |
-
if (current_modified > self.last_modified or
|
| 72 |
-
current_size != self.last_size):
|
| 73 |
-
|
| 74 |
-
self.last_modified = current_modified
|
| 75 |
-
self.last_size = current_size
|
| 76 |
-
|
| 77 |
-
# Lê o conteúdo novo
|
| 78 |
-
try:
|
| 79 |
-
with open(self.file_path, 'r', encoding='utf-8') as f:
|
| 80 |
-
content = f.read()
|
| 81 |
-
self.callback(content)
|
| 82 |
-
return True
|
| 83 |
-
except Exception as e:
|
| 84 |
-
logging.error(f"Erro ao ler arquivo: {e}")
|
| 85 |
-
|
| 86 |
-
except Exception as e:
|
| 87 |
-
logging.error(f"Erro ao verificar arquivo: {e}")
|
| 88 |
-
|
| 89 |
-
return False
|
| 90 |
-
|
| 91 |
-
class RealTimeProcessor:
|
| 92 |
-
"""Processador de dados em tempo real do bot"""
|
| 93 |
-
|
| 94 |
-
def __init__(self, config: RealTimeConfig):
|
| 95 |
-
self.config = config
|
| 96 |
-
self.log_parser = VampireBotLogParser()
|
| 97 |
-
self.technical_engine = TechnicalAnalysisEngine()
|
| 98 |
-
self.event_queue = queue.Queue(maxsize=config.max_queue_size)
|
| 99 |
-
self.subscribers = []
|
| 100 |
-
self.running = False
|
| 101 |
-
self.last_analysis: Optional[BotAnalysis] = None
|
| 102 |
-
|
| 103 |
-
# Setup logging
|
| 104 |
-
self.logger = logging.getLogger(__name__)
|
| 105 |
-
|
| 106 |
-
def subscribe(self, callback: Callable[[BotEvent], None]):
|
| 107 |
-
"""Inscreve um callback para receber eventos"""
|
| 108 |
-
self.subscribers.append(callback)
|
| 109 |
-
|
| 110 |
-
def unsubscribe(self, callback: Callable[[BotEvent], None]):
|
| 111 |
-
"""Remove um callback da lista de inscritos"""
|
| 112 |
-
if callback in self.subscribers:
|
| 113 |
-
self.subscribers.remove(callback)
|
| 114 |
-
|
| 115 |
-
def _notify_subscribers(self, event: BotEvent):
|
| 116 |
-
"""Notifica todos os inscritos sobre um evento"""
|
| 117 |
-
for callback in self.subscribers:
|
| 118 |
-
try:
|
| 119 |
-
callback(event)
|
| 120 |
-
except Exception as e:
|
| 121 |
-
self.logger.error(f"Erro ao notificar subscriber: {e}")
|
| 122 |
-
|
| 123 |
-
def _process_new_log_data(self, log_content: str):
|
| 124 |
-
"""Processa novos dados de log"""
|
| 125 |
-
try:
|
| 126 |
-
# Parse do log
|
| 127 |
-
bot_analysis = self.log_parser.parse_log(log_content)
|
| 128 |
-
|
| 129 |
-
if bot_analysis:
|
| 130 |
-
# Verifica se é uma nova análise
|
| 131 |
-
is_new_analysis = (
|
| 132 |
-
self.last_analysis is None or
|
| 133 |
-
bot_analysis.timestamp != self.last_analysis.timestamp
|
| 134 |
-
)
|
| 135 |
-
|
| 136 |
-
if is_new_analysis:
|
| 137 |
-
# Cria evento de nova análise
|
| 138 |
-
event = BotEvent(
|
| 139 |
-
timestamp=datetime.now(),
|
| 140 |
-
event_type='new_analysis',
|
| 141 |
-
data=asdict(bot_analysis),
|
| 142 |
-
priority='normal'
|
| 143 |
-
)
|
| 144 |
-
|
| 145 |
-
# Adiciona à fila de eventos
|
| 146 |
-
try:
|
| 147 |
-
self.event_queue.put_nowait(event)
|
| 148 |
-
except queue.Full:
|
| 149 |
-
self.logger.warning("Fila de eventos cheia, removendo evento mais antigo")
|
| 150 |
-
try:
|
| 151 |
-
self.event_queue.get_nowait()
|
| 152 |
-
self.event_queue.put_nowait(event)
|
| 153 |
-
except queue.Empty:
|
| 154 |
-
pass
|
| 155 |
-
|
| 156 |
-
# Verifica alertas de Fibonacci
|
| 157 |
-
if bot_analysis.fibonacci_analysis and bot_analysis.fibonacci_analysis.alerts:
|
| 158 |
-
fib_event = BotEvent(
|
| 159 |
-
timestamp=datetime.now(),
|
| 160 |
-
event_type='fibonacci_alert',
|
| 161 |
-
data={
|
| 162 |
-
'alerts': bot_analysis.fibonacci_analysis.alerts,
|
| 163 |
-
'signal': bot_analysis.fibonacci_analysis.signal,
|
| 164 |
-
'confidence': bot_analysis.fibonacci_analysis.confidence
|
| 165 |
-
},
|
| 166 |
-
priority='high'
|
| 167 |
-
)
|
| 168 |
-
|
| 169 |
-
try:
|
| 170 |
-
self.event_queue.put_nowait(fib_event)
|
| 171 |
-
except queue.Full:
|
| 172 |
-
pass
|
| 173 |
-
|
| 174 |
-
# Verifica mudança de sinal
|
| 175 |
-
if (self.last_analysis and
|
| 176 |
-
bot_analysis.fibonacci_analysis and
|
| 177 |
-
self.last_analysis.fibonacci_analysis and
|
| 178 |
-
bot_analysis.fibonacci_analysis.signal != self.last_analysis.fibonacci_analysis.signal):
|
| 179 |
-
|
| 180 |
-
signal_event = BotEvent(
|
| 181 |
-
timestamp=datetime.now(),
|
| 182 |
-
event_type='signal_change',
|
| 183 |
-
data={
|
| 184 |
-
'old_signal': self.last_analysis.fibonacci_analysis.signal,
|
| 185 |
-
'new_signal': bot_analysis.fibonacci_analysis.signal,
|
| 186 |
-
'confidence': bot_analysis.fibonacci_analysis.confidence
|
| 187 |
-
},
|
| 188 |
-
priority='critical'
|
| 189 |
-
)
|
| 190 |
-
|
| 191 |
-
try:
|
| 192 |
-
self.event_queue.put_nowait(signal_event)
|
| 193 |
-
except queue.Full:
|
| 194 |
-
pass
|
| 195 |
-
|
| 196 |
-
self.last_analysis = bot_analysis
|
| 197 |
-
|
| 198 |
-
except Exception as e:
|
| 199 |
-
self.logger.error(f"Erro ao processar log: {e}")
|
| 200 |
-
|
| 201 |
-
def _event_processor_loop(self):
|
| 202 |
-
"""Loop principal de processamento de eventos"""
|
| 203 |
-
while self.running:
|
| 204 |
-
try:
|
| 205 |
-
# Processa eventos da fila
|
| 206 |
-
try:
|
| 207 |
-
event = self.event_queue.get(timeout=0.1)
|
| 208 |
-
self._notify_subscribers(event)
|
| 209 |
-
self.event_queue.task_done()
|
| 210 |
-
except queue.Empty:
|
| 211 |
-
continue
|
| 212 |
-
|
| 213 |
-
except Exception as e:
|
| 214 |
-
self.logger.error(f"Erro no loop de eventos: {e}")
|
| 215 |
-
time.sleep(0.1)
|
| 216 |
-
|
| 217 |
-
def start(self):
|
| 218 |
-
"""Inicia o processamento em tempo real"""
|
| 219 |
-
if self.running:
|
| 220 |
-
return
|
| 221 |
-
|
| 222 |
-
self.running = True
|
| 223 |
-
self.logger.info("Iniciando processamento em tempo real")
|
| 224 |
-
|
| 225 |
-
# Inicia thread de processamento de eventos
|
| 226 |
-
self.event_thread = Thread(target=self._event_processor_loop, daemon=True)
|
| 227 |
-
self.event_thread.start()
|
| 228 |
-
|
| 229 |
-
# Configura watcher de arquivo
|
| 230 |
-
self.file_watcher = FileWatcher(
|
| 231 |
-
self.config.log_file_path,
|
| 232 |
-
self._process_new_log_data
|
| 233 |
-
)
|
| 234 |
-
self.file_watcher.start()
|
| 235 |
-
|
| 236 |
-
# Inicia thread de monitoramento
|
| 237 |
-
self.monitor_thread = Thread(target=self._monitor_loop, daemon=True)
|
| 238 |
-
self.monitor_thread.start()
|
| 239 |
-
|
| 240 |
-
def stop(self):
|
| 241 |
-
"""Para o processamento em tempo real"""
|
| 242 |
-
if not self.running:
|
| 243 |
-
return
|
| 244 |
-
|
| 245 |
-
self.logger.info("Parando processamento em tempo real")
|
| 246 |
-
self.running = False
|
| 247 |
-
|
| 248 |
-
if hasattr(self, 'file_watcher'):
|
| 249 |
-
self.file_watcher.stop()
|
| 250 |
-
|
| 251 |
-
def _monitor_loop(self):
|
| 252 |
-
"""Loop de monitoramento de arquivo"""
|
| 253 |
-
while self.running:
|
| 254 |
-
try:
|
| 255 |
-
self.file_watcher.check_changes()
|
| 256 |
-
time.sleep(self.config.check_interval)
|
| 257 |
-
except Exception as e:
|
| 258 |
-
self.logger.error(f"Erro no monitoramento: {e}")
|
| 259 |
-
time.sleep(1)
|
| 260 |
-
|
| 261 |
-
def get_status(self) -> Dict[str, Any]:
|
| 262 |
-
"""Retorna status do processador"""
|
| 263 |
-
return {
|
| 264 |
-
'running': self.running,
|
| 265 |
-
'queue_size': self.event_queue.qsize(),
|
| 266 |
-
'subscribers_count': len(self.subscribers),
|
| 267 |
-
'last_analysis_time': self.last_analysis.timestamp if self.last_analysis else None,
|
| 268 |
-
'config': asdict(self.config)
|
| 269 |
-
}
|
| 270 |
-
|
| 271 |
-
class RealTimeIntegration:
|
| 272 |
-
"""Sistema principal de integração em tempo real"""
|
| 273 |
-
|
| 274 |
-
def __init__(self, log_file_path: str):
|
| 275 |
-
self.config = RealTimeConfig(log_file_path=log_file_path)
|
| 276 |
-
self.processor = RealTimeProcessor(self.config)
|
| 277 |
-
self.event_history = []
|
| 278 |
-
self.max_history = 1000
|
| 279 |
-
|
| 280 |
-
# Setup logging
|
| 281 |
-
self.logger = logging.getLogger(__name__)
|
| 282 |
-
|
| 283 |
-
# Inscreve callback padrão
|
| 284 |
-
self.processor.subscribe(self._default_event_handler)
|
| 285 |
-
|
| 286 |
-
def _default_event_handler(self, event: BotEvent):
|
| 287 |
-
"""Handler padrão para eventos"""
|
| 288 |
-
# Adiciona ao histórico
|
| 289 |
-
self.event_history.append(event)
|
| 290 |
-
|
| 291 |
-
# Mantém tamanho do histórico
|
| 292 |
-
if len(self.event_history) > self.max_history:
|
| 293 |
-
self.event_history = self.event_history[-self.max_history:]
|
| 294 |
-
|
| 295 |
-
# Log do evento
|
| 296 |
-
self.logger.info(f"Evento {event.event_type} - Prioridade: {event.priority}")
|
| 297 |
-
|
| 298 |
-
# Processamento específico por tipo
|
| 299 |
-
if event.event_type == 'signal_change':
|
| 300 |
-
self.logger.warning(
|
| 301 |
-
f"MUDANÇA DE SINAL: {event.data['old_signal']} -> {event.data['new_signal']} "
|
| 302 |
-
f"(Confiança: {event.data['confidence']}%)"
|
| 303 |
-
)
|
| 304 |
-
elif event.event_type == 'fibonacci_alert':
|
| 305 |
-
self.logger.info(f"Alerta Fibonacci: {len(event.data['alerts'])} alertas")
|
| 306 |
-
|
| 307 |
-
def start(self):
|
| 308 |
-
"""Inicia a integração em tempo real"""
|
| 309 |
-
self.processor.start()
|
| 310 |
-
self.logger.info(f"Integração em tempo real iniciada para: {self.config.log_file_path}")
|
| 311 |
-
|
| 312 |
-
def stop(self):
|
| 313 |
-
"""Para a integração em tempo real"""
|
| 314 |
-
self.processor.stop()
|
| 315 |
-
self.logger.info("Integração em tempo real parada")
|
| 316 |
-
|
| 317 |
-
def get_recent_events(self, limit: int = 10) -> list[BotEvent]:
|
| 318 |
-
"""Retorna eventos recentes"""
|
| 319 |
-
return self.event_history[-limit:] if self.event_history else []
|
| 320 |
-
|
| 321 |
-
def get_status(self) -> Dict[str, Any]:
|
| 322 |
-
"""Retorna status completo do sistema"""
|
| 323 |
-
processor_status = self.processor.get_status()
|
| 324 |
-
return {
|
| 325 |
-
**processor_status,
|
| 326 |
-
'event_history_size': len(self.event_history),
|
| 327 |
-
'recent_events': len([e for e in self.event_history if
|
| 328 |
-
(datetime.now() - e.timestamp).seconds < 300]) # últimos 5 min
|
| 329 |
-
}
|
| 330 |
-
|
| 331 |
-
# Exemplo de uso
|
| 332 |
-
if __name__ == "__main__":
|
| 333 |
-
# Configurar logging
|
| 334 |
-
logging.basicConfig(level=logging.INFO)
|
| 335 |
-
|
| 336 |
-
# Criar integração
|
| 337 |
-
integration = RealTimeIntegration("d:/hugging_face_spaces/text")
|
| 338 |
-
|
| 339 |
-
# Callback personalizado
|
| 340 |
-
def custom_handler(event: BotEvent):
|
| 341 |
-
print(f"[{event.timestamp}] {event.event_type}: {event.priority}")
|
| 342 |
-
|
| 343 |
-
integration.processor.subscribe(custom_handler)
|
| 344 |
-
|
| 345 |
-
try:
|
| 346 |
-
# Iniciar
|
| 347 |
-
integration.start()
|
| 348 |
-
|
| 349 |
-
# Manter rodando
|
| 350 |
-
while True:
|
| 351 |
-
time.sleep(1)
|
| 352 |
-
status = integration.get_status()
|
| 353 |
-
if status['recent_events'] > 0:
|
| 354 |
-
print(f"Eventos recentes: {status['recent_events']}")
|
| 355 |
-
|
| 356 |
-
except KeyboardInterrupt:
|
| 357 |
-
print("Parando integração...")
|
| 358 |
-
integration.stop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
|
@@ -1,6 +1,39 @@
|
|
| 1 |
-
|
|
|
|
| 2 |
transformers==4.36.0
|
| 3 |
torch>=2.0.0
|
| 4 |
numpy>=1.24.0
|
| 5 |
pandas>=2.0.0
|
| 6 |
-
scipy>=1.11.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Core dependencies
|
| 2 |
+
gradio==5.44.0
|
| 3 |
transformers==4.36.0
|
| 4 |
torch>=2.0.0
|
| 5 |
numpy>=1.24.0
|
| 6 |
pandas>=2.0.0
|
| 7 |
+
scipy>=1.11.0
|
| 8 |
+
|
| 9 |
+
# Machine Learning
|
| 10 |
+
scikit-learn>=1.3.0
|
| 11 |
+
xgboost>=1.7.0
|
| 12 |
+
lightgbm>=4.0.0
|
| 13 |
+
|
| 14 |
+
# Data processing
|
| 15 |
+
ta>=0.10.2
|
| 16 |
+
yfinance>=0.2.18
|
| 17 |
+
requests>=2.31.0
|
| 18 |
+
|
| 19 |
+
# Visualization
|
| 20 |
+
matplotlib>=3.7.0
|
| 21 |
+
plotly>=5.15.0
|
| 22 |
+
seaborn>=0.12.0
|
| 23 |
+
|
| 24 |
+
# Utilities
|
| 25 |
+
tqdm>=4.65.0
|
| 26 |
+
joblib>=1.3.0
|
| 27 |
+
psutil>=5.9.0
|
| 28 |
+
|
| 29 |
+
# Development and testing
|
| 30 |
+
pytest>=7.4.0
|
| 31 |
+
pytest-cov>=4.1.0
|
| 32 |
+
flake8>=6.0.0
|
| 33 |
+
black>=23.0.0
|
| 34 |
+
|
| 35 |
+
# Optional dependencies for advanced features
|
| 36 |
+
# Uncomment if needed:
|
| 37 |
+
# tensorflow>=2.13.0
|
| 38 |
+
# keras>=2.13.0
|
| 39 |
+
# optuna>=3.3.0
|
sentiment_analysis.py
DELETED
|
@@ -1,331 +0,0 @@
|
|
| 1 |
-
"""Módulo de análise de sentimento usando IA financeira."""
|
| 2 |
-
|
| 3 |
-
import re
|
| 4 |
-
from typing import Dict, Optional, Any
|
| 5 |
-
from dataclasses import dataclass
|
| 6 |
-
|
| 7 |
-
from config import FINANCIAL_MODELS, AIConfig, AppConfig
|
| 8 |
-
|
| 9 |
-
# Importações opcionais para IA
|
| 10 |
-
try:
|
| 11 |
-
from transformers import pipeline
|
| 12 |
-
import torch
|
| 13 |
-
TRANSFORMERS_AVAILABLE = True
|
| 14 |
-
except ImportError:
|
| 15 |
-
TRANSFORMERS_AVAILABLE = False
|
| 16 |
-
print(AppConfig.STATUS_MESSAGES['AI_UNAVAILABLE'])
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
@dataclass
|
| 20 |
-
class SentimentResult:
|
| 21 |
-
"""Classe para representar resultado de análise de sentimento."""
|
| 22 |
-
sentiment: str # 'positive', 'negative', 'neutral'
|
| 23 |
-
confidence: float # 0.0 - 1.0
|
| 24 |
-
label: str # 'POSITIVO', 'NEGATIVO', 'NEUTRO'
|
| 25 |
-
model_used: Optional[str] = None
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
class ModelManager:
|
| 29 |
-
"""Gerenciador de modelos de IA."""
|
| 30 |
-
|
| 31 |
-
def __init__(self):
|
| 32 |
-
self.sentiment_pipeline = None
|
| 33 |
-
self.current_model_info = None
|
| 34 |
-
self.is_available = TRANSFORMERS_AVAILABLE
|
| 35 |
-
|
| 36 |
-
if self.is_available:
|
| 37 |
-
self._load_models()
|
| 38 |
-
|
| 39 |
-
def _load_models(self) -> None:
|
| 40 |
-
"""Tenta carregar modelos em ordem de prioridade."""
|
| 41 |
-
for model_config in FINANCIAL_MODELS:
|
| 42 |
-
try:
|
| 43 |
-
print(AppConfig.STATUS_MESSAGES['AI_LOADING'].format(
|
| 44 |
-
model_config['description']
|
| 45 |
-
))
|
| 46 |
-
|
| 47 |
-
self.sentiment_pipeline = pipeline(
|
| 48 |
-
AIConfig.PIPELINE_CONFIG['task'],
|
| 49 |
-
model=model_config["name"],
|
| 50 |
-
return_all_scores=AIConfig.PIPELINE_CONFIG['return_all_scores']
|
| 51 |
-
)
|
| 52 |
-
|
| 53 |
-
self.current_model_info = model_config
|
| 54 |
-
print(AppConfig.STATUS_MESSAGES['AI_SUCCESS'].format(
|
| 55 |
-
model_config['description']
|
| 56 |
-
))
|
| 57 |
-
break
|
| 58 |
-
|
| 59 |
-
except Exception as e:
|
| 60 |
-
print(AppConfig.STATUS_MESSAGES['AI_FAILED'].format(
|
| 61 |
-
model_config['name'], str(e)
|
| 62 |
-
))
|
| 63 |
-
continue
|
| 64 |
-
|
| 65 |
-
if self.sentiment_pipeline is None:
|
| 66 |
-
print(AppConfig.STATUS_MESSAGES['NO_MODEL_LOADED'])
|
| 67 |
-
self.is_available = False
|
| 68 |
-
|
| 69 |
-
def get_model_info(self) -> Optional[Dict[str, Any]]:
|
| 70 |
-
"""Retorna informações do modelo atual."""
|
| 71 |
-
return self.current_model_info
|
| 72 |
-
|
| 73 |
-
def is_model_available(self) -> bool:
|
| 74 |
-
"""Verifica se há modelo disponível."""
|
| 75 |
-
return self.is_available and self.sentiment_pipeline is not None
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
class TextPreprocessor:
|
| 79 |
-
"""Pré-processador de texto para análise de sentimento."""
|
| 80 |
-
|
| 81 |
-
@staticmethod
|
| 82 |
-
def clean_text(text: str) -> str:
|
| 83 |
-
"""Limpa e prepara texto para análise."""
|
| 84 |
-
if not text:
|
| 85 |
-
return ""
|
| 86 |
-
|
| 87 |
-
# Remover caracteres especiais, manter apenas palavras, espaços e alguns símbolos
|
| 88 |
-
clean_text = re.sub(r'[^\w\s\+\-\%\.]', ' ', text)
|
| 89 |
-
|
| 90 |
-
# Limitar tamanho para o modelo
|
| 91 |
-
clean_text = clean_text[:AIConfig.MAX_TEXT_LENGTH]
|
| 92 |
-
|
| 93 |
-
# Remover espaços extras
|
| 94 |
-
clean_text = ' '.join(clean_text.split())
|
| 95 |
-
|
| 96 |
-
return clean_text
|
| 97 |
-
|
| 98 |
-
@staticmethod
|
| 99 |
-
def extract_financial_keywords(text: str) -> Dict[str, int]:
|
| 100 |
-
"""Extrai palavras-chave financeiras do texto."""
|
| 101 |
-
financial_keywords = {
|
| 102 |
-
'positive': ['alta', 'subida', 'ganho', 'lucro', 'crescimento', 'otimista', 'positivo'],
|
| 103 |
-
'negative': ['baixa', 'queda', 'perda', 'prejuízo', 'declínio', 'pessimista', 'negativo'],
|
| 104 |
-
'neutral': ['estável', 'neutro', 'lateral', 'consolidação']
|
| 105 |
-
}
|
| 106 |
-
|
| 107 |
-
text_lower = text.lower()
|
| 108 |
-
keyword_counts = {'positive': 0, 'negative': 0, 'neutral': 0}
|
| 109 |
-
|
| 110 |
-
for category, keywords in financial_keywords.items():
|
| 111 |
-
for keyword in keywords:
|
| 112 |
-
keyword_counts[category] += text_lower.count(keyword)
|
| 113 |
-
|
| 114 |
-
return keyword_counts
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
class SentimentAnalyzer:
|
| 118 |
-
"""Analisador de sentimento principal."""
|
| 119 |
-
|
| 120 |
-
def __init__(self, model_manager: ModelManager):
|
| 121 |
-
self.model_manager = model_manager
|
| 122 |
-
self.preprocessor = TextPreprocessor()
|
| 123 |
-
|
| 124 |
-
def analyze(self, text: str) -> SentimentResult:
|
| 125 |
-
"""Analisa o sentimento do texto."""
|
| 126 |
-
if not self.model_manager.is_model_available():
|
| 127 |
-
return self._get_fallback_sentiment(text)
|
| 128 |
-
|
| 129 |
-
try:
|
| 130 |
-
# Pré-processar texto
|
| 131 |
-
clean_text = self.preprocessor.clean_text(text)
|
| 132 |
-
|
| 133 |
-
if not clean_text.strip():
|
| 134 |
-
return SentimentResult(
|
| 135 |
-
sentiment='neutral',
|
| 136 |
-
confidence=0.5,
|
| 137 |
-
label='NEUTRO',
|
| 138 |
-
model_used='fallback'
|
| 139 |
-
)
|
| 140 |
-
|
| 141 |
-
# Executar análise de sentimento
|
| 142 |
-
result = self.model_manager.sentiment_pipeline(clean_text)
|
| 143 |
-
|
| 144 |
-
# Processar resultado
|
| 145 |
-
return self._process_model_result(result)
|
| 146 |
-
|
| 147 |
-
except Exception as e:
|
| 148 |
-
print(f"Erro na análise de sentimento: {e}")
|
| 149 |
-
return self._get_fallback_sentiment(text)
|
| 150 |
-
|
| 151 |
-
def _process_model_result(self, result: Any) -> SentimentResult:
|
| 152 |
-
"""Processa resultado do modelo de IA."""
|
| 153 |
-
try:
|
| 154 |
-
# Processar resultado baseado no formato
|
| 155 |
-
if isinstance(result, list) and len(result) > 0:
|
| 156 |
-
# Se return_all_scores=True, pegar o resultado com maior score
|
| 157 |
-
if isinstance(result[0], list):
|
| 158 |
-
predictions = result[0]
|
| 159 |
-
best_prediction = max(predictions, key=lambda x: x['score'])
|
| 160 |
-
else:
|
| 161 |
-
best_prediction = result[0]
|
| 162 |
-
|
| 163 |
-
# Mapear label usando o mapeamento do modelo atual
|
| 164 |
-
label = best_prediction['label']
|
| 165 |
-
confidence = best_prediction['score']
|
| 166 |
-
|
| 167 |
-
# Usar mapeamento específico do modelo ou fallback genérico
|
| 168 |
-
model_info = self.model_manager.get_model_info()
|
| 169 |
-
if model_info and label in model_info['labels']:
|
| 170 |
-
sentiment_label = model_info['labels'][label]
|
| 171 |
-
else:
|
| 172 |
-
# Fallback para mapeamento genérico
|
| 173 |
-
sentiment_label = self._map_generic_label(label)
|
| 174 |
-
|
| 175 |
-
return SentimentResult(
|
| 176 |
-
sentiment=label.lower(),
|
| 177 |
-
confidence=confidence,
|
| 178 |
-
label=sentiment_label,
|
| 179 |
-
model_used=model_info['name'] if model_info else 'unknown'
|
| 180 |
-
)
|
| 181 |
-
|
| 182 |
-
# Fallback se resultado não esperado
|
| 183 |
-
return SentimentResult(
|
| 184 |
-
sentiment='neutral',
|
| 185 |
-
confidence=0.5,
|
| 186 |
-
label='NEUTRO',
|
| 187 |
-
model_used='fallback'
|
| 188 |
-
)
|
| 189 |
-
|
| 190 |
-
except Exception as e:
|
| 191 |
-
print(f"Erro ao processar resultado do modelo: {e}")
|
| 192 |
-
return SentimentResult(
|
| 193 |
-
sentiment='neutral',
|
| 194 |
-
confidence=0.5,
|
| 195 |
-
label='NEUTRO',
|
| 196 |
-
model_used='error_fallback'
|
| 197 |
-
)
|
| 198 |
-
|
| 199 |
-
def _map_generic_label(self, label: str) -> str:
|
| 200 |
-
"""Mapeia labels genéricos para formato padrão."""
|
| 201 |
-
label_lower = label.lower()
|
| 202 |
-
|
| 203 |
-
if 'neg' in label_lower or 'bad' in label_lower:
|
| 204 |
-
return 'NEGATIVO'
|
| 205 |
-
elif 'pos' in label_lower or 'good' in label_lower:
|
| 206 |
-
return 'POSITIVO'
|
| 207 |
-
else:
|
| 208 |
-
return 'NEUTRO'
|
| 209 |
-
|
| 210 |
-
def _get_fallback_sentiment(self, text: str) -> SentimentResult:
|
| 211 |
-
"""Análise de sentimento baseada em palavras-chave (fallback)."""
|
| 212 |
-
if not text:
|
| 213 |
-
return SentimentResult(
|
| 214 |
-
sentiment='neutral',
|
| 215 |
-
confidence=0.5,
|
| 216 |
-
label='NEUTRO',
|
| 217 |
-
model_used='keyword_fallback'
|
| 218 |
-
)
|
| 219 |
-
|
| 220 |
-
# Análise baseada em palavras-chave
|
| 221 |
-
keyword_counts = self.preprocessor.extract_financial_keywords(text)
|
| 222 |
-
|
| 223 |
-
total_keywords = sum(keyword_counts.values())
|
| 224 |
-
if total_keywords == 0:
|
| 225 |
-
return SentimentResult(
|
| 226 |
-
sentiment='neutral',
|
| 227 |
-
confidence=0.5,
|
| 228 |
-
label='NEUTRO',
|
| 229 |
-
model_used='keyword_fallback'
|
| 230 |
-
)
|
| 231 |
-
|
| 232 |
-
# Determinar sentimento dominante
|
| 233 |
-
max_category = max(keyword_counts, key=keyword_counts.get)
|
| 234 |
-
max_count = keyword_counts[max_category]
|
| 235 |
-
confidence = min(0.8, max_count / total_keywords) # Máximo 80% de confiança
|
| 236 |
-
|
| 237 |
-
sentiment_mapping = {
|
| 238 |
-
'positive': ('positive', 'POSITIVO'),
|
| 239 |
-
'negative': ('negative', 'NEGATIVO'),
|
| 240 |
-
'neutral': ('neutral', 'NEUTRO')
|
| 241 |
-
}
|
| 242 |
-
|
| 243 |
-
sentiment, label = sentiment_mapping[max_category]
|
| 244 |
-
|
| 245 |
-
return SentimentResult(
|
| 246 |
-
sentiment=sentiment,
|
| 247 |
-
confidence=confidence,
|
| 248 |
-
label=label,
|
| 249 |
-
model_used='keyword_fallback'
|
| 250 |
-
)
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
class SentimentScorer:
|
| 254 |
-
"""Calculador de pontuação baseada em sentimento."""
|
| 255 |
-
|
| 256 |
-
@staticmethod
|
| 257 |
-
def calculate_sentiment_score(sentiment_result: SentimentResult) -> int:
|
| 258 |
-
"""Calcula pontuação de confiança baseada no sentimento."""
|
| 259 |
-
from config import ScoringConfig
|
| 260 |
-
|
| 261 |
-
base_score = int(sentiment_result.confidence * ScoringConfig.SENTIMENT_MAX_SCORE)
|
| 262 |
-
|
| 263 |
-
# Bonificação por modelo de IA vs fallback
|
| 264 |
-
if sentiment_result.model_used and 'fallback' not in sentiment_result.model_used:
|
| 265 |
-
base_score = int(base_score * 1.2) # 20% de bonificação para modelos de IA
|
| 266 |
-
|
| 267 |
-
return min(base_score, ScoringConfig.SENTIMENT_MAX_SCORE)
|
| 268 |
-
|
| 269 |
-
@staticmethod
|
| 270 |
-
def get_sentiment_signal_description(sentiment_result: SentimentResult) -> str:
|
| 271 |
-
"""Gera descrição do sinal de sentimento."""
|
| 272 |
-
confidence_pct = sentiment_result.confidence * 100
|
| 273 |
-
|
| 274 |
-
if sentiment_result.label == 'POSITIVO':
|
| 275 |
-
bias = "viés de COMPRA"
|
| 276 |
-
elif sentiment_result.label == 'NEGATIVO':
|
| 277 |
-
bias = "viés de VENDA"
|
| 278 |
-
else:
|
| 279 |
-
bias = "sem viés claro"
|
| 280 |
-
|
| 281 |
-
model_indicator = "🤖 IA" if 'fallback' not in (sentiment_result.model_used or '') else "📝 Palavras-chave"
|
| 282 |
-
|
| 283 |
-
return f"{model_indicator} Sentimento: {sentiment_result.label} ({confidence_pct:.1f}%): {bias}"
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
class SentimentAnalysisEngine:
|
| 287 |
-
"""Engine principal de análise de sentimento."""
|
| 288 |
-
|
| 289 |
-
def __init__(self):
|
| 290 |
-
self.model_manager = ModelManager()
|
| 291 |
-
self.analyzer = SentimentAnalyzer(self.model_manager)
|
| 292 |
-
self.scorer = SentimentScorer()
|
| 293 |
-
|
| 294 |
-
def analyze_text(self, text: str) -> Dict[str, Any]:
|
| 295 |
-
"""Executa análise completa de sentimento."""
|
| 296 |
-
# Análise de sentimento
|
| 297 |
-
sentiment_result = self.analyzer.analyze(text)
|
| 298 |
-
|
| 299 |
-
# Calcular pontuação
|
| 300 |
-
score = self.scorer.calculate_sentiment_score(sentiment_result)
|
| 301 |
-
|
| 302 |
-
# Gerar descrição
|
| 303 |
-
description = self.scorer.get_sentiment_signal_description(sentiment_result)
|
| 304 |
-
|
| 305 |
-
return {
|
| 306 |
-
'result': sentiment_result,
|
| 307 |
-
'score': score,
|
| 308 |
-
'description': description
|
| 309 |
-
}
|
| 310 |
-
|
| 311 |
-
def get_model_status(self) -> Dict[str, Any]:
|
| 312 |
-
"""Retorna status do modelo atual."""
|
| 313 |
-
if self.model_manager.is_model_available():
|
| 314 |
-
model_info = self.model_manager.get_model_info()
|
| 315 |
-
return {
|
| 316 |
-
'available': True,
|
| 317 |
-
'model_name': model_info['name'] if model_info else 'Unknown',
|
| 318 |
-
'description': model_info['description'] if model_info else 'Unknown Model',
|
| 319 |
-
'status': 'active'
|
| 320 |
-
}
|
| 321 |
-
else:
|
| 322 |
-
return {
|
| 323 |
-
'available': False,
|
| 324 |
-
'model_name': None,
|
| 325 |
-
'description': 'IA indisponível - usando análise por palavras-chave',
|
| 326 |
-
'status': 'fallback'
|
| 327 |
-
}
|
| 328 |
-
|
| 329 |
-
def is_available(self) -> bool:
|
| 330 |
-
"""Verifica se análise de IA está disponível."""
|
| 331 |
-
return self.model_manager.is_model_available()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
advanced_market_processing.py → src/core/advanced_market_processing.py
RENAMED
|
File without changes
|
src/ui/gradio_interface.py
CHANGED
|
@@ -13,6 +13,12 @@ from src.utils.utils import (
|
|
| 13 |
FormatUtils
|
| 14 |
)
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
class UIComponents:
|
| 18 |
"""Componentes da interface do usuário."""
|
|
@@ -568,6 +574,7 @@ class GradioInterface:
|
|
| 568 |
self.model_info = model_info or {'available': False}
|
| 569 |
self.interface = None
|
| 570 |
|
|
|
|
| 571 |
def analyze_market(self, symbol, price, volume, sentiment_text):
|
| 572 |
"""Função principal de análise"""
|
| 573 |
try:
|
|
@@ -597,6 +604,7 @@ class GradioInterface:
|
|
| 597 |
except Exception as e:
|
| 598 |
return f"❌ Erro na análise: {str(e)}"
|
| 599 |
|
|
|
|
| 600 |
def analyze_bot_log(self, log_content):
|
| 601 |
"""Função para analisar logs do bot externo"""
|
| 602 |
try:
|
|
@@ -689,6 +697,7 @@ class GradioInterface:
|
|
| 689 |
self.interface = interface
|
| 690 |
return interface
|
| 691 |
|
|
|
|
| 692 |
def _analyze_wrapper(self, market_input: str) -> Tuple[str, str, str, str, str, str, str, Dict[str, Any]]:
|
| 693 |
"""Wrapper para função de análise com formatação de saída."""
|
| 694 |
try:
|
|
@@ -745,6 +754,7 @@ class GradioInterface:
|
|
| 745 |
{'error': error_msg}
|
| 746 |
)
|
| 747 |
|
|
|
|
| 748 |
def _analyze_manual_wrapper(self, symbol: str, price: float, volume: float, sentiment_text: str) -> Tuple[str, str, str, str, str, str, str, Dict[str, Any]]:
|
| 749 |
"""Wrapper para análise manual com formatação de saída."""
|
| 750 |
try:
|
|
|
|
| 13 |
FormatUtils
|
| 14 |
)
|
| 15 |
|
| 16 |
+
# Importar sistema de logging
|
| 17 |
+
try:
|
| 18 |
+
from src.utils.request_logger import log_requests_responses
|
| 19 |
+
except ImportError:
|
| 20 |
+
log_requests_responses = None
|
| 21 |
+
|
| 22 |
|
| 23 |
class UIComponents:
|
| 24 |
"""Componentes da interface do usuário."""
|
|
|
|
| 574 |
self.model_info = model_info or {'available': False}
|
| 575 |
self.interface = None
|
| 576 |
|
| 577 |
+
@log_requests_responses("analyze_market") if log_requests_responses else lambda f: f
|
| 578 |
def analyze_market(self, symbol, price, volume, sentiment_text):
|
| 579 |
"""Função principal de análise"""
|
| 580 |
try:
|
|
|
|
| 604 |
except Exception as e:
|
| 605 |
return f"❌ Erro na análise: {str(e)}"
|
| 606 |
|
| 607 |
+
@log_requests_responses("analyze_bot_log") if log_requests_responses else lambda f: f
|
| 608 |
def analyze_bot_log(self, log_content):
|
| 609 |
"""Função para analisar logs do bot externo"""
|
| 610 |
try:
|
|
|
|
| 697 |
self.interface = interface
|
| 698 |
return interface
|
| 699 |
|
| 700 |
+
@log_requests_responses("_analyze_wrapper") if log_requests_responses else lambda f: f
|
| 701 |
def _analyze_wrapper(self, market_input: str) -> Tuple[str, str, str, str, str, str, str, Dict[str, Any]]:
|
| 702 |
"""Wrapper para função de análise com formatação de saída."""
|
| 703 |
try:
|
|
|
|
| 754 |
{'error': error_msg}
|
| 755 |
)
|
| 756 |
|
| 757 |
+
@log_requests_responses("_analyze_manual_wrapper") if log_requests_responses else lambda f: f
|
| 758 |
def _analyze_manual_wrapper(self, symbol: str, price: float, volume: float, sentiment_text: str) -> Tuple[str, str, str, str, str, str, str, Dict[str, Any]]:
|
| 759 |
"""Wrapper para análise manual com formatação de saída."""
|
| 760 |
try:
|
src/utils/__pycache__/request_logger.cpython-313.pyc
ADDED
|
Binary file (8.85 kB). View file
|
|
|
src/utils/request_logger.py
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Módulo de logging para requisições e respostas HTTP."""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import time
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Dict, Any, Optional
|
| 7 |
+
from functools import wraps
|
| 8 |
+
import inspect
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class RequestResponseLogger:
|
| 12 |
+
"""Logger para capturar dados de requisições e respostas."""
|
| 13 |
+
|
| 14 |
+
def __init__(self, enabled: bool = True):
|
| 15 |
+
"""Inicializa o logger.
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
enabled: Se o logging está habilitado
|
| 19 |
+
"""
|
| 20 |
+
self.enabled = enabled
|
| 21 |
+
self.request_counter = 0
|
| 22 |
+
|
| 23 |
+
def log_request(self, endpoint: str, data: Dict[str, Any], method: str = "POST") -> str:
|
| 24 |
+
"""Registra dados da requisição recebida.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
endpoint: Nome do endpoint/função chamada
|
| 28 |
+
data: Dados recebidos na requisição
|
| 29 |
+
method: Método HTTP (GET, POST, etc.)
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
ID único da requisição
|
| 33 |
+
"""
|
| 34 |
+
if not self.enabled:
|
| 35 |
+
return ""
|
| 36 |
+
|
| 37 |
+
self.request_counter += 1
|
| 38 |
+
request_id = f"REQ_{self.request_counter}_{int(time.time())}"
|
| 39 |
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
| 40 |
+
|
| 41 |
+
print("\n" + "="*80)
|
| 42 |
+
print(f"📥 REQUISIÇÃO RECEBIDA [{request_id}]")
|
| 43 |
+
print(f"⏰ Timestamp: {timestamp}")
|
| 44 |
+
print(f"🎯 Endpoint: {endpoint}")
|
| 45 |
+
print(f"🔧 Método: {method}")
|
| 46 |
+
print("📋 Dados Recebidos:")
|
| 47 |
+
|
| 48 |
+
# Formatar dados de forma legível
|
| 49 |
+
try:
|
| 50 |
+
formatted_data = json.dumps(data, indent=2, ensure_ascii=False, default=str)
|
| 51 |
+
print(formatted_data)
|
| 52 |
+
except Exception as e:
|
| 53 |
+
print(f" {data} (Erro na formatação JSON: {e})")
|
| 54 |
+
|
| 55 |
+
print("="*80)
|
| 56 |
+
|
| 57 |
+
return request_id
|
| 58 |
+
|
| 59 |
+
def log_response(self, request_id: str, response_data: Any, processing_time: float = None) -> None:
|
| 60 |
+
"""Registra dados da resposta enviada.
|
| 61 |
+
|
| 62 |
+
Args:
|
| 63 |
+
request_id: ID da requisição correspondente
|
| 64 |
+
response_data: Dados enviados como resposta
|
| 65 |
+
processing_time: Tempo de processamento em segundos
|
| 66 |
+
"""
|
| 67 |
+
if not self.enabled:
|
| 68 |
+
return
|
| 69 |
+
|
| 70 |
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
| 71 |
+
|
| 72 |
+
print("\n" + "-"*80)
|
| 73 |
+
print(f"📤 RESPOSTA ENVIADA [{request_id}]")
|
| 74 |
+
print(f"⏰ Timestamp: {timestamp}")
|
| 75 |
+
|
| 76 |
+
if processing_time is not None:
|
| 77 |
+
print(f"⚡ Tempo de Processamento: {processing_time:.3f}s")
|
| 78 |
+
|
| 79 |
+
print("📋 Dados Enviados:")
|
| 80 |
+
|
| 81 |
+
# Formatar resposta de forma legível
|
| 82 |
+
try:
|
| 83 |
+
if isinstance(response_data, (dict, list)):
|
| 84 |
+
formatted_response = json.dumps(response_data, indent=2, ensure_ascii=False, default=str)
|
| 85 |
+
print(formatted_response)
|
| 86 |
+
elif isinstance(response_data, str) and len(response_data) > 500:
|
| 87 |
+
# Para strings muito longas, mostrar apenas o início e fim
|
| 88 |
+
print(f" [STRING LONGA - {len(response_data)} caracteres]")
|
| 89 |
+
print(f" Início: {response_data[:200]}...")
|
| 90 |
+
print(f" Fim: ...{response_data[-200:]}")
|
| 91 |
+
else:
|
| 92 |
+
print(f" {response_data}")
|
| 93 |
+
except Exception as e:
|
| 94 |
+
print(f" {response_data} (Erro na formatação: {e})")
|
| 95 |
+
|
| 96 |
+
print("-"*80 + "\n")
|
| 97 |
+
|
| 98 |
+
def log_error(self, request_id: str, error: Exception, processing_time: float = None) -> None:
|
| 99 |
+
"""Registra erro durante o processamento.
|
| 100 |
+
|
| 101 |
+
Args:
|
| 102 |
+
request_id: ID da requisição correspondente
|
| 103 |
+
error: Exceção ocorrida
|
| 104 |
+
processing_time: Tempo até o erro em segundos
|
| 105 |
+
"""
|
| 106 |
+
if not self.enabled:
|
| 107 |
+
return
|
| 108 |
+
|
| 109 |
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
| 110 |
+
|
| 111 |
+
print("\n" + "!"*80)
|
| 112 |
+
print(f"❌ ERRO NO PROCESSAMENTO [{request_id}]")
|
| 113 |
+
print(f"⏰ Timestamp: {timestamp}")
|
| 114 |
+
|
| 115 |
+
if processing_time is not None:
|
| 116 |
+
print(f"⚡ Tempo até Erro: {processing_time:.3f}s")
|
| 117 |
+
|
| 118 |
+
print(f"🚨 Tipo do Erro: {type(error).__name__}")
|
| 119 |
+
print(f"📝 Mensagem: {str(error)}")
|
| 120 |
+
print("!"*80 + "\n")
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
# Instância global do logger
|
| 124 |
+
request_logger = RequestResponseLogger()
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def log_requests_responses(endpoint_name: str = None):
|
| 128 |
+
"""Decorator para automaticamente logar requisições e respostas de funções.
|
| 129 |
+
|
| 130 |
+
Args:
|
| 131 |
+
endpoint_name: Nome personalizado para o endpoint (opcional)
|
| 132 |
+
"""
|
| 133 |
+
def decorator(func):
|
| 134 |
+
@wraps(func)
|
| 135 |
+
def wrapper(*args, **kwargs):
|
| 136 |
+
if not request_logger.enabled:
|
| 137 |
+
return func(*args, **kwargs)
|
| 138 |
+
|
| 139 |
+
# Determinar nome do endpoint
|
| 140 |
+
endpoint = endpoint_name or func.__name__
|
| 141 |
+
|
| 142 |
+
# Capturar dados da requisição
|
| 143 |
+
request_data = {
|
| 144 |
+
'args': args,
|
| 145 |
+
'kwargs': kwargs,
|
| 146 |
+
'function_signature': str(inspect.signature(func))
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
# Logar requisição
|
| 150 |
+
request_id = request_logger.log_request(endpoint, request_data)
|
| 151 |
+
|
| 152 |
+
# Executar função e medir tempo
|
| 153 |
+
start_time = time.time()
|
| 154 |
+
try:
|
| 155 |
+
result = func(*args, **kwargs)
|
| 156 |
+
processing_time = time.time() - start_time
|
| 157 |
+
|
| 158 |
+
# Logar resposta
|
| 159 |
+
request_logger.log_response(request_id, result, processing_time)
|
| 160 |
+
|
| 161 |
+
return result
|
| 162 |
+
|
| 163 |
+
except Exception as e:
|
| 164 |
+
processing_time = time.time() - start_time
|
| 165 |
+
|
| 166 |
+
# Logar erro
|
| 167 |
+
request_logger.log_error(request_id, e, processing_time)
|
| 168 |
+
|
| 169 |
+
# Re-raise a exceção
|
| 170 |
+
raise
|
| 171 |
+
|
| 172 |
+
return wrapper
|
| 173 |
+
return decorator
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def enable_logging():
|
| 177 |
+
"""Habilita o logging de requisições/respostas."""
|
| 178 |
+
request_logger.enabled = True
|
| 179 |
+
print("✅ Logging de requisições/respostas HABILITADO")
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def disable_logging():
|
| 183 |
+
"""Desabilita o logging de requisições/respostas."""
|
| 184 |
+
request_logger.enabled = False
|
| 185 |
+
print("❌ Logging de requisições/respostas DESABILITADO")
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def get_logging_status() -> bool:
|
| 189 |
+
"""Retorna o status atual do logging.
|
| 190 |
+
|
| 191 |
+
Returns:
|
| 192 |
+
True se o logging está habilitado, False caso contrário
|
| 193 |
+
"""
|
| 194 |
+
return request_logger.enabled
|
ui.py
DELETED
|
@@ -1,818 +0,0 @@
|
|
| 1 |
-
"""Módulo de interface do usuário com Gradio."""
|
| 2 |
-
|
| 3 |
-
import gradio as gr
|
| 4 |
-
from typing import Dict, Any, Optional, Tuple
|
| 5 |
-
|
| 6 |
-
from config import UIConfig, AppConfig
|
| 7 |
-
from utils import (
|
| 8 |
-
DateTimeUtils,
|
| 9 |
-
NumberUtils,
|
| 10 |
-
ConfidenceUtils,
|
| 11 |
-
ActionUtils,
|
| 12 |
-
SentimentUtils,
|
| 13 |
-
FormatUtils
|
| 14 |
-
)
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
class UIComponents:
|
| 18 |
-
"""Componentes da interface do usuário."""
|
| 19 |
-
|
| 20 |
-
@staticmethod
|
| 21 |
-
def create_header() -> str:
|
| 22 |
-
"""
|
| 23 |
-
|
| 24 |
-
@staticmethod
|
| 25 |
-
def format_harmonic_patterns(analysis_result: Dict[str, Any]) -> str:
|
| 26 |
-
"""Formata padrões harmônicos detectados."""
|
| 27 |
-
harmonic_patterns = analysis_result.get('harmonic_patterns', [])
|
| 28 |
-
|
| 29 |
-
if not harmonic_patterns:
|
| 30 |
-
return """
|
| 31 |
-
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 32 |
-
<h3 style="color: #495057; margin-top: 0;">🎵 Padrões Harmônicos</h3>
|
| 33 |
-
<div style="text-align: center; padding: 30px; color: #6c757d;">
|
| 34 |
-
<div style="font-size: 2em; margin-bottom: 10px;">📊</div>
|
| 35 |
-
<p>Nenhum padrão harmônico detectado</p>
|
| 36 |
-
</div>
|
| 37 |
-
</div>
|
| 38 |
-
"""
|
| 39 |
-
|
| 40 |
-
patterns_html = ""
|
| 41 |
-
for pattern in harmonic_patterns:
|
| 42 |
-
pattern_name = pattern.get('name', 'Desconhecido')
|
| 43 |
-
confidence = pattern.get('confidence', 0)
|
| 44 |
-
direction = pattern.get('direction', 'NEUTRO')
|
| 45 |
-
|
| 46 |
-
direction_emoji = "📈" if direction == "ALTA" else "📉" if direction == "BAIXA" else "➡️"
|
| 47 |
-
|
| 48 |
-
patterns_html += f"""
|
| 49 |
-
<div style="background: #f8f9fa; border-radius: 6px; padding: 15px; margin-bottom: 10px; border-left: 4px solid #007bff;">
|
| 50 |
-
<div style="display: flex; justify-content: space-between; align-items: center;">
|
| 51 |
-
<div>
|
| 52 |
-
<strong style="color: #495057;">{direction_emoji} {pattern_name}</strong>
|
| 53 |
-
<div style="color: #6c757d; font-size: 0.9em;">Direção: {direction}</div>
|
| 54 |
-
</div>
|
| 55 |
-
<div style="text-align: right;">
|
| 56 |
-
<div style="font-weight: bold; color: #007bff;">{confidence}%</div>
|
| 57 |
-
<div style="color: #6c757d; font-size: 0.8em;">Confiança</div>
|
| 58 |
-
</div>
|
| 59 |
-
</div>
|
| 60 |
-
</div>
|
| 61 |
-
"""
|
| 62 |
-
|
| 63 |
-
return f"""
|
| 64 |
-
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 65 |
-
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #007bff; padding-bottom: 10px;">
|
| 66 |
-
🎵 Padrões Harmônicos Detectados
|
| 67 |
-
</h3>
|
| 68 |
-
{patterns_html}
|
| 69 |
-
</div>
|
| 70 |
-
"""
|
| 71 |
-
|
| 72 |
-
@staticmethod
|
| 73 |
-
def format_fibonacci_alerts(analysis_result: Dict[str, Any]) -> str:
|
| 74 |
-
"""Formata alertas de Fibonacci."""
|
| 75 |
-
fibonacci_data = analysis_result.get('fibonacci_analysis', {})
|
| 76 |
-
|
| 77 |
-
if not fibonacci_data:
|
| 78 |
-
return """
|
| 79 |
-
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 80 |
-
<h3 style="color: #495057; margin-top: 0;">📐 Análise de Fibonacci</h3>
|
| 81 |
-
<div style="text-align: center; padding: 30px; color: #6c757d;">
|
| 82 |
-
<div style="font-size: 2em; margin-bottom: 10px;">📊</div>
|
| 83 |
-
<p>Dados de Fibonacci não disponíveis</p>
|
| 84 |
-
</div>
|
| 85 |
-
</div>
|
| 86 |
-
"""
|
| 87 |
-
|
| 88 |
-
levels = fibonacci_data.get('levels', [])
|
| 89 |
-
current_level = fibonacci_data.get('current_level', 'N/A')
|
| 90 |
-
support_resistance = fibonacci_data.get('support_resistance', {})
|
| 91 |
-
|
| 92 |
-
levels_html = ""
|
| 93 |
-
for level in levels:
|
| 94 |
-
level_value = level.get('level', 0)
|
| 95 |
-
price = level.get('price', 0)
|
| 96 |
-
status = level.get('status', 'NEUTRO')
|
| 97 |
-
|
| 98 |
-
status_color = {
|
| 99 |
-
'SUPORTE': '#28a745',
|
| 100 |
-
'RESISTENCIA': '#dc3545',
|
| 101 |
-
'NEUTRO': '#6c757d'
|
| 102 |
-
}.get(status, '#6c757d')
|
| 103 |
-
|
| 104 |
-
levels_html += f"""
|
| 105 |
-
<div style="display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #eee;">
|
| 106 |
-
<span style="color: #495057;">{level_value}%</span>
|
| 107 |
-
<span style="font-weight: bold; color: #495057;">{NumberUtils.format_price(price)}</span>
|
| 108 |
-
<span style="color: {status_color}; font-weight: 600;">{status}</span>
|
| 109 |
-
</div>
|
| 110 |
-
"""
|
| 111 |
-
|
| 112 |
-
return f"""
|
| 113 |
-
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 114 |
-
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #ffc107; padding-bottom: 10px;">
|
| 115 |
-
📐 Análise de Fibonacci
|
| 116 |
-
</h3>
|
| 117 |
-
|
| 118 |
-
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; padding: 15px; margin-bottom: 15px;">
|
| 119 |
-
<strong style="color: #856404;">Nível Atual: {current_level}</strong>
|
| 120 |
-
</div>
|
| 121 |
-
|
| 122 |
-
<h4 style="color: #495057; margin-bottom: 10px;">📊 Níveis de Fibonacci</h4>
|
| 123 |
-
<div style="background: #f8f9fa; border-radius: 6px; padding: 15px;">
|
| 124 |
-
<div style="display: flex; justify-content: space-between; font-weight: bold; color: #495057; border-bottom: 2px solid #dee2e6; padding-bottom: 8px; margin-bottom: 10px;">
|
| 125 |
-
<span>Nível</span>
|
| 126 |
-
<span>Preço</span>
|
| 127 |
-
<span>Status</span>
|
| 128 |
-
</div>
|
| 129 |
-
{levels_html}
|
| 130 |
-
</div>
|
| 131 |
-
</div>
|
| 132 |
-
"""
|
| 133 |
-
|
| 134 |
-
@staticmethod
|
| 135 |
-
def format_bot_analysis_result(analysis_result: Dict[str, Any]) -> str:
|
| 136 |
-
"""Formata resultado específico da análise do bot externo."""
|
| 137 |
-
try:
|
| 138 |
-
bot_data = analysis_result.get('bot_data', {})
|
| 139 |
-
fibonacci_alerts = bot_data.get('fibonacci_alerts', 0)
|
| 140 |
-
fibonacci_signal = bot_data.get('fibonacci_signal', 'UNKNOWN')
|
| 141 |
-
technical_indicators = bot_data.get('technical_indicators', {})
|
| 142 |
-
|
| 143 |
-
# Formatação do sinal Fibonacci
|
| 144 |
-
signal_color = {
|
| 145 |
-
'BUY': '#28a745',
|
| 146 |
-
'SELL': '#dc3545',
|
| 147 |
-
'HOLD': '#ffc107',
|
| 148 |
-
'UNKNOWN': '#6c757d'
|
| 149 |
-
}.get(fibonacci_signal, '#6c757d')
|
| 150 |
-
|
| 151 |
-
signal_emoji = {
|
| 152 |
-
'BUY': '📈',
|
| 153 |
-
'SELL': '📉',
|
| 154 |
-
'HOLD': '⏸️',
|
| 155 |
-
'UNKNOWN': '❓'
|
| 156 |
-
}.get(fibonacci_signal, '❓')
|
| 157 |
-
|
| 158 |
-
# Indicadores técnicos do bot
|
| 159 |
-
indicators_html = ""
|
| 160 |
-
for indicator, value in technical_indicators.items():
|
| 161 |
-
if value is not None:
|
| 162 |
-
indicators_html += f"""
|
| 163 |
-
<div style="display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #eee;">
|
| 164 |
-
<span style="color: #495057; text-transform: uppercase;">{indicator}</span>
|
| 165 |
-
<span style="font-weight: bold; color: #495057;">{value}</span>
|
| 166 |
-
</div>
|
| 167 |
-
"""
|
| 168 |
-
|
| 169 |
-
return f"""
|
| 170 |
-
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; padding: 25px; color: white; margin-bottom: 20px;">
|
| 171 |
-
<h2 style="margin-top: 0; display: flex; align-items: center; gap: 10px;">
|
| 172 |
-
🤖 Análise do Bot de Trading
|
| 173 |
-
</h2>
|
| 174 |
-
|
| 175 |
-
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px;">
|
| 176 |
-
<div style="background: rgba(255,255,255,0.1); border-radius: 8px; padding: 15px;">
|
| 177 |
-
<h4 style="margin-top: 0; color: #f8f9fa;">📐 Fibonacci</h4>
|
| 178 |
-
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
|
| 179 |
-
<span style="font-size: 1.5em;">{signal_emoji}</span>
|
| 180 |
-
<div>
|
| 181 |
-
<div style="font-weight: bold; font-size: 1.1em; color: {signal_color};">{fibonacci_signal}</div>
|
| 182 |
-
<div style="font-size: 0.9em; opacity: 0.8;">Sinal Atual</div>
|
| 183 |
-
</div>
|
| 184 |
-
</div>
|
| 185 |
-
<div style="font-size: 0.9em; opacity: 0.8;">Alertas: {fibonacci_alerts}</div>
|
| 186 |
-
</div>
|
| 187 |
-
|
| 188 |
-
<div style="background: rgba(255,255,255,0.1); border-radius: 8px; padding: 15px;">
|
| 189 |
-
<h4 style="margin-top: 0; color: #f8f9fa;">📊 Indicadores</h4>
|
| 190 |
-
<div style="font-size: 0.9em;">
|
| 191 |
-
{indicators_html if indicators_html else '<div style="opacity: 0.8;">Dados não disponíveis</div>'}
|
| 192 |
-
</div>
|
| 193 |
-
</div>
|
| 194 |
-
</div>
|
| 195 |
-
</div>
|
| 196 |
-
|
| 197 |
-
{ResultFormatter.format_main_result(analysis_result)}
|
| 198 |
-
"""
|
| 199 |
-
|
| 200 |
-
except Exception as e:
|
| 201 |
-
return f"""
|
| 202 |
-
<div style="background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 8px; padding: 15px; color: #721c24;">
|
| 203 |
-
<h4 style="margin-top: 0;">❌ Erro na Formatação</h4>
|
| 204 |
-
<p style="margin: 0;">Erro ao formatar resultado do bot: {str(e)}</p>
|
| 205 |
-
</div>
|
| 206 |
-
"""
|
| 207 |
-
|
| 208 |
-
@staticmethod
|
| 209 |
-
def create_header() -> str:
|
| 210 |
-
"""Cria cabeçalho da aplicação."""
|
| 211 |
-
return f"""
|
| 212 |
-
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; margin-bottom: 20px;">
|
| 213 |
-
<h1 style="color: white; margin: 0; font-size: 2.5em; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">
|
| 214 |
-
📈 {AppConfig.APP_TITLE}
|
| 215 |
-
</h1>
|
| 216 |
-
<p style="color: #f0f0f0; margin: 10px 0 0 0; font-size: 1.2em;">
|
| 217 |
-
{AppConfig.APP_DESCRIPTION}
|
| 218 |
-
</p>
|
| 219 |
-
</div>
|
| 220 |
-
"""
|
| 221 |
-
|
| 222 |
-
@staticmethod
|
| 223 |
-
def create_input_section() -> gr.Column:
|
| 224 |
-
"""Cria seção de entrada de dados."""
|
| 225 |
-
with gr.Column() as input_section:
|
| 226 |
-
gr.HTML("""
|
| 227 |
-
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; border-left: 4px solid #007bff;">
|
| 228 |
-
<h3 style="margin: 0 0 10px 0; color: #495057;">📊 Dados de Mercado</h3>
|
| 229 |
-
<p style="margin: 0; color: #6c757d; font-size: 0.9em;">
|
| 230 |
-
Cole os dados do ativo ou digite manualmente os valores
|
| 231 |
-
</p>
|
| 232 |
-
</div>
|
| 233 |
-
""")
|
| 234 |
-
|
| 235 |
-
# Abas para diferentes tipos de entrada
|
| 236 |
-
with gr.Tabs():
|
| 237 |
-
with gr.Tab("📊 Análise Manual"):
|
| 238 |
-
symbol_input = gr.Textbox(
|
| 239 |
-
label="Símbolo do Ativo",
|
| 240 |
-
placeholder="Ex: BTCUSDT",
|
| 241 |
-
lines=1
|
| 242 |
-
)
|
| 243 |
-
price_input = gr.Number(
|
| 244 |
-
label="Preço Atual",
|
| 245 |
-
placeholder="Ex: 45000.50"
|
| 246 |
-
)
|
| 247 |
-
volume_input = gr.Number(
|
| 248 |
-
label="Volume (Opcional)",
|
| 249 |
-
placeholder="Ex: 1000000"
|
| 250 |
-
)
|
| 251 |
-
sentiment_input = gr.Textbox(
|
| 252 |
-
label="Texto para Análise de Sentimento (Opcional)",
|
| 253 |
-
placeholder="Ex: Notícias ou comentários sobre o ativo",
|
| 254 |
-
lines=3
|
| 255 |
-
)
|
| 256 |
-
analyze_manual_btn = gr.Button(
|
| 257 |
-
"🔍 Analisar Manualmente",
|
| 258 |
-
variant="primary",
|
| 259 |
-
size="lg"
|
| 260 |
-
)
|
| 261 |
-
|
| 262 |
-
with gr.Tab("🤖 Log do Bot"):
|
| 263 |
-
market_input = gr.Textbox(
|
| 264 |
-
label="Log do Bot de Trading",
|
| 265 |
-
placeholder=AppConfig.EXAMPLE_INPUT,
|
| 266 |
-
lines=8,
|
| 267 |
-
max_lines=15
|
| 268 |
-
)
|
| 269 |
-
analyze_btn = gr.Button(
|
| 270 |
-
"🔍 Analisar Log do Bot",
|
| 271 |
-
variant="primary",
|
| 272 |
-
size="lg"
|
| 273 |
-
)
|
| 274 |
-
|
| 275 |
-
return input_section, market_input, analyze_btn, symbol_input, price_input, volume_input, sentiment_input, analyze_manual_btn
|
| 276 |
-
|
| 277 |
-
@staticmethod
|
| 278 |
-
def create_output_section() -> Tuple[gr.Column, Dict[str, Any]]:
|
| 279 |
-
"""Cria seção de saída de resultados."""
|
| 280 |
-
outputs = {}
|
| 281 |
-
|
| 282 |
-
with gr.Column() as output_section:
|
| 283 |
-
# Status do modelo de IA
|
| 284 |
-
outputs['ai_status'] = gr.HTML(
|
| 285 |
-
UIComponents._get_ai_status_html(available=False)
|
| 286 |
-
)
|
| 287 |
-
|
| 288 |
-
# Resultado principal
|
| 289 |
-
outputs['main_result'] = gr.HTML()
|
| 290 |
-
|
| 291 |
-
# Abas de detalhes
|
| 292 |
-
with gr.Tabs():
|
| 293 |
-
# Aba de Análise Técnica
|
| 294 |
-
with gr.Tab("📊 Análise Técnica"):
|
| 295 |
-
outputs['technical_analysis'] = gr.HTML()
|
| 296 |
-
|
| 297 |
-
# Aba de Análise de Sentimento
|
| 298 |
-
with gr.Tab("🧠 Análise de Sentimento"):
|
| 299 |
-
outputs['sentiment_analysis'] = gr.HTML()
|
| 300 |
-
|
| 301 |
-
# Aba de Recomendações
|
| 302 |
-
with gr.Tab("💡 Recomendações"):
|
| 303 |
-
outputs['recommendations'] = gr.HTML()
|
| 304 |
-
|
| 305 |
-
# Aba de Padrões Harmônicos
|
| 306 |
-
with gr.Tab("🎵 Padrões Harmônicos"):
|
| 307 |
-
outputs['harmonic_patterns'] = gr.HTML()
|
| 308 |
-
|
| 309 |
-
# Aba de Alertas Fibonacci
|
| 310 |
-
with gr.Tab("📐 Fibonacci"):
|
| 311 |
-
outputs['fibonacci_alerts'] = gr.HTML()
|
| 312 |
-
|
| 313 |
-
# Aba de Dados Brutos
|
| 314 |
-
with gr.Tab("🔍 Dados Detalhados"):
|
| 315 |
-
outputs['raw_data'] = gr.JSON()
|
| 316 |
-
|
| 317 |
-
# Adicionar novos outputs ao dicionário
|
| 318 |
-
if 'harmonic_patterns' not in outputs:
|
| 319 |
-
outputs['harmonic_patterns'] = gr.HTML()
|
| 320 |
-
if 'fibonacci_alerts' not in outputs:
|
| 321 |
-
outputs['fibonacci_alerts'] = gr.HTML()
|
| 322 |
-
|
| 323 |
-
return output_section, outputs
|
| 324 |
-
|
| 325 |
-
@staticmethod
|
| 326 |
-
def create_footer(model_info: Optional[Dict[str, Any]] = None) -> str:
|
| 327 |
-
"""Cria rodapé da aplicação."""
|
| 328 |
-
timestamp = DateTimeUtils.get_current_datetime()
|
| 329 |
-
|
| 330 |
-
if model_info and model_info.get('available', False):
|
| 331 |
-
ai_status = f"🤖 IA: {model_info.get('description', 'Modelo Ativo')}"
|
| 332 |
-
else:
|
| 333 |
-
ai_status = "⚠️ IA: Indisponível (apenas análise técnica)"
|
| 334 |
-
|
| 335 |
-
return f"""
|
| 336 |
-
<div style="text-align: center; padding: 15px; background: #f8f9fa; border-radius: 8px; margin-top: 20px; border-top: 2px solid #dee2e6;">
|
| 337 |
-
<p style="margin: 0; color: #6c757d; font-size: 0.9em;">
|
| 338 |
-
{ai_status} | ⏰ Última atualização: {timestamp}
|
| 339 |
-
</p>
|
| 340 |
-
<p style="margin: 5px 0 0 0; color: #adb5bd; font-size: 0.8em;">
|
| 341 |
-
Desenvolvido para análise de scalping no mercado financeiro
|
| 342 |
-
</p>
|
| 343 |
-
</div>
|
| 344 |
-
"""
|
| 345 |
-
|
| 346 |
-
@staticmethod
|
| 347 |
-
def _get_ai_status_html(available: bool, model_description: str = "") -> str:
|
| 348 |
-
"""Gera HTML para status da IA."""
|
| 349 |
-
if available:
|
| 350 |
-
return f"""
|
| 351 |
-
<div style="background: #d4edda; border: 1px solid #c3e6cb; border-radius: 8px; padding: 12px; margin-bottom: 15px;">
|
| 352 |
-
<div style="display: flex; align-items: center; gap: 10px;">
|
| 353 |
-
<span style="font-size: 1.2em;">🤖</span>
|
| 354 |
-
<div>
|
| 355 |
-
<strong style="color: #155724;">IA Ativa:</strong>
|
| 356 |
-
<span style="color: #155724;">{model_description}</span>
|
| 357 |
-
</div>
|
| 358 |
-
</div>
|
| 359 |
-
</div>
|
| 360 |
-
"""
|
| 361 |
-
else:
|
| 362 |
-
return """
|
| 363 |
-
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 12px; margin-bottom: 15px;">
|
| 364 |
-
<div style="display: flex; align-items: center; gap: 10px;">
|
| 365 |
-
<span style="font-size: 1.2em;">⚠️</span>
|
| 366 |
-
<div>
|
| 367 |
-
<strong style="color: #856404;">IA Indisponível:</strong>
|
| 368 |
-
<span style="color: #856404;">Executando apenas análise técnica</span>
|
| 369 |
-
</div>
|
| 370 |
-
</div>
|
| 371 |
-
</div>
|
| 372 |
-
"""
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
class ResultFormatter:
|
| 376 |
-
"""Formatador de resultados para a interface."""
|
| 377 |
-
|
| 378 |
-
@staticmethod
|
| 379 |
-
def format_main_result(analysis_result: Dict[str, Any]) -> str:
|
| 380 |
-
"""Formata resultado principal da análise."""
|
| 381 |
-
action = analysis_result.get('action', 'AGUARDAR')
|
| 382 |
-
confidence = analysis_result.get('confidence', 0)
|
| 383 |
-
market_data = analysis_result.get('market_data', {})
|
| 384 |
-
|
| 385 |
-
# Obter informações da ação
|
| 386 |
-
action_emojis = ActionUtils.get_action_emojis(action)
|
| 387 |
-
action_color = ActionUtils.get_action_color(action)
|
| 388 |
-
confidence_level = ConfidenceUtils.get_confidence_level(confidence)
|
| 389 |
-
confidence_bar = ConfidenceUtils.generate_confidence_bar(confidence)
|
| 390 |
-
|
| 391 |
-
# Formatação do preço
|
| 392 |
-
price = market_data.get('price', 0)
|
| 393 |
-
variation = market_data.get('variation', 0)
|
| 394 |
-
formatted_price = NumberUtils.format_price(price)
|
| 395 |
-
formatted_variation = NumberUtils.format_percentage(variation)
|
| 396 |
-
|
| 397 |
-
# Cor da variação
|
| 398 |
-
variation_color = "#28a745" if variation >= 0 else "#dc3545"
|
| 399 |
-
|
| 400 |
-
return f"""
|
| 401 |
-
<div style="background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 12px; padding: 25px; margin: 15px 0; border: 2px solid #dee2e6;">
|
| 402 |
-
<div style="text-align: center; margin-bottom: 20px;">
|
| 403 |
-
<div style="font-size: 3em; margin-bottom: 10px;">{action_emojis['main']}</div>
|
| 404 |
-
<h2 style="margin: 0; color: {action_color}; font-size: 2em; text-transform: uppercase; letter-spacing: 1px;">
|
| 405 |
-
{action_emojis['action']} {action}
|
| 406 |
-
</h2>
|
| 407 |
-
</div>
|
| 408 |
-
|
| 409 |
-
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
|
| 410 |
-
<div style="text-align: center; padding: 15px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
| 411 |
-
<div style="font-size: 0.9em; color: #6c757d; margin-bottom: 5px;">PREÇO ATUAL</div>
|
| 412 |
-
<div style="font-size: 1.8em; font-weight: bold; color: #495057;">{formatted_price}</div>
|
| 413 |
-
<div style="font-size: 1.1em; color: {variation_color}; font-weight: 600;">{formatted_variation}</div>
|
| 414 |
-
</div>
|
| 415 |
-
|
| 416 |
-
<div style="text-align: center; padding: 15px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
| 417 |
-
<div style="font-size: 0.9em; color: #6c757d; margin-bottom: 5px;">CONFIANÇA</div>
|
| 418 |
-
<div style="font-size: 1.8em; font-weight: bold; color: #495057;">{confidence}%</div>
|
| 419 |
-
<div style="font-size: 0.9em; color: #6c757d;">{confidence_level}</div>
|
| 420 |
-
</div>
|
| 421 |
-
</div>
|
| 422 |
-
|
| 423 |
-
<div style="text-align: center; margin-top: 15px;">
|
| 424 |
-
<div style="font-size: 0.9em; color: #6c757d; margin-bottom: 8px;">NÍVEL DE CONFIANÇA</div>
|
| 425 |
-
<div style="font-family: monospace; font-size: 1.2em; letter-spacing: 2px; color: #495057;">{confidence_bar}</div>
|
| 426 |
-
</div>
|
| 427 |
-
</div>
|
| 428 |
-
"""
|
| 429 |
-
|
| 430 |
-
@staticmethod
|
| 431 |
-
def format_technical_analysis(analysis_result: Dict[str, Any]) -> str:
|
| 432 |
-
"""Formata análise técnica."""
|
| 433 |
-
market_data = analysis_result.get('market_data', {})
|
| 434 |
-
signals = analysis_result.get('signals', [])
|
| 435 |
-
|
| 436 |
-
# Resumo dos dados de mercado
|
| 437 |
-
market_summary = FormatUtils.format_market_summary(market_data)
|
| 438 |
-
|
| 439 |
-
# Lista de sinais
|
| 440 |
-
signals_list = FormatUtils.format_signal_list(signals)
|
| 441 |
-
|
| 442 |
-
return f"""
|
| 443 |
-
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 444 |
-
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #007bff; padding-bottom: 10px;">
|
| 445 |
-
📊 Indicadores Técnicos
|
| 446 |
-
</h3>
|
| 447 |
-
|
| 448 |
-
<div style="background: #f8f9fa; padding: 15px; border-radius: 6px; margin-bottom: 20px;">
|
| 449 |
-
{market_summary}
|
| 450 |
-
</div>
|
| 451 |
-
|
| 452 |
-
<h4 style="color: #495057; margin-bottom: 15px;">🎯 Sinais Detectados</h4>
|
| 453 |
-
<div style="background: #f8f9fa; padding: 15px; border-radius: 6px; font-family: monospace; white-space: pre-line;">
|
| 454 |
-
{signals_list}
|
| 455 |
-
</div>
|
| 456 |
-
</div>
|
| 457 |
-
"""
|
| 458 |
-
|
| 459 |
-
@staticmethod
|
| 460 |
-
def format_sentiment_analysis(analysis_result: Dict[str, Any]) -> str:
|
| 461 |
-
"""Formata análise de sentimento."""
|
| 462 |
-
sentiment = analysis_result.get('sentiment', {})
|
| 463 |
-
|
| 464 |
-
if not sentiment:
|
| 465 |
-
return """
|
| 466 |
-
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 467 |
-
<h3 style="color: #495057; margin-top: 0;">🧠 Análise de Sentimento</h3>
|
| 468 |
-
<div style="text-align: center; padding: 30px; color: #6c757d;">
|
| 469 |
-
<div style="font-size: 2em; margin-bottom: 10px;">⚠️</div>
|
| 470 |
-
<p>Análise de sentimento não disponível</p>
|
| 471 |
-
<p style="font-size: 0.9em;">Instale as dependências de IA para ativar esta funcionalidade</p>
|
| 472 |
-
</div>
|
| 473 |
-
</div>
|
| 474 |
-
"""
|
| 475 |
-
|
| 476 |
-
label = sentiment.get('label', 'NEUTRO')
|
| 477 |
-
confidence = sentiment.get('confidence', 0)
|
| 478 |
-
emoji = SentimentUtils.get_sentiment_emoji(label)
|
| 479 |
-
|
| 480 |
-
# Cor baseada no sentimento
|
| 481 |
-
sentiment_colors = {
|
| 482 |
-
'POSITIVO': '#28a745',
|
| 483 |
-
'NEGATIVO': '#dc3545',
|
| 484 |
-
'NEUTRO': '#ffc107'
|
| 485 |
-
}
|
| 486 |
-
color = sentiment_colors.get(label, '#6c757d')
|
| 487 |
-
|
| 488 |
-
return f"""
|
| 489 |
-
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 490 |
-
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #28a745; padding-bottom: 10px;">
|
| 491 |
-
🧠 Análise de Sentimento
|
| 492 |
-
</h3>
|
| 493 |
-
|
| 494 |
-
<div style="text-align: center; padding: 20px;">
|
| 495 |
-
<div style="font-size: 3em; margin-bottom: 15px;">{emoji}</div>
|
| 496 |
-
<h4 style="color: {color}; margin: 0; font-size: 1.5em; text-transform: uppercase;">{label}</h4>
|
| 497 |
-
<div style="margin-top: 10px; color: #6c757d;">Confiança: {confidence:.1f}%</div>
|
| 498 |
-
</div>
|
| 499 |
-
|
| 500 |
-
<div style="background: #f8f9fa; padding: 15px; border-radius: 6px; margin-top: 15px;">
|
| 501 |
-
<h5 style="margin-top: 0; color: #495057;">📝 Detalhes da Análise</h5>
|
| 502 |
-
<p style="margin: 0; color: #6c757d; font-size: 0.9em;">
|
| 503 |
-
O modelo de IA analisou o contexto do mercado e determinou um sentimento <strong>{label.lower()}</strong>
|
| 504 |
-
com {confidence:.1f}% de confiança.
|
| 505 |
-
</p>
|
| 506 |
-
</div>
|
| 507 |
-
</div>
|
| 508 |
-
"""
|
| 509 |
-
|
| 510 |
-
@staticmethod
|
| 511 |
-
def format_recommendations(analysis_result: Dict[str, Any]) -> str:
|
| 512 |
-
"""Formata recomendações de trading."""
|
| 513 |
-
action = analysis_result.get('action', 'AGUARDAR')
|
| 514 |
-
market_data = analysis_result.get('market_data', {})
|
| 515 |
-
price = market_data.get('price', 0)
|
| 516 |
-
|
| 517 |
-
# Recomendações de trading
|
| 518 |
-
trading_recs = FormatUtils.format_trading_recommendations(action, price)
|
| 519 |
-
|
| 520 |
-
# Direção de trading
|
| 521 |
-
direction = ActionUtils.get_trading_direction(action)
|
| 522 |
-
direction_emoji = "📈" if direction == "COMPRA" else "📉" if direction == "VENDA" else "⏸️"
|
| 523 |
-
|
| 524 |
-
return f"""
|
| 525 |
-
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 526 |
-
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #ffc107; padding-bottom: 10px;">
|
| 527 |
-
💡 Recomendações de Trading
|
| 528 |
-
</h3>
|
| 529 |
-
|
| 530 |
-
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; padding: 15px; margin-bottom: 20px;">
|
| 531 |
-
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
|
| 532 |
-
<span style="font-size: 1.5em;">{direction_emoji}</span>
|
| 533 |
-
<strong style="color: #856404;">Direção: {direction}</strong>
|
| 534 |
-
</div>
|
| 535 |
-
</div>
|
| 536 |
-
|
| 537 |
-
<div style="background: #f8f9fa; padding: 15px; border-radius: 6px; white-space: pre-line;">
|
| 538 |
-
{trading_recs}
|
| 539 |
-
</div>
|
| 540 |
-
|
| 541 |
-
<div style="background: #d1ecf1; border: 1px solid #bee5eb; border-radius: 6px; padding: 15px; margin-top: 15px;">
|
| 542 |
-
<h5 style="margin-top: 0; color: #0c5460;">⚠️ Aviso Importante</h5>
|
| 543 |
-
<p style="margin: 0; color: #0c5460; font-size: 0.9em;">
|
| 544 |
-
Esta análise é apenas para fins educacionais. Sempre faça sua própria pesquisa e
|
| 545 |
-
considere consultar um consultor financeiro antes de tomar decisões de investimento.
|
| 546 |
-
</p>
|
| 547 |
-
</div>
|
| 548 |
-
</div>
|
| 549 |
-
"""
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
class GradioInterface:
|
| 553 |
-
"""Interface principal do Gradio."""
|
| 554 |
-
|
| 555 |
-
def __init__(self, analysis_function, model_info: Optional[Dict[str, Any]] = None):
|
| 556 |
-
"""Inicializa interface."""
|
| 557 |
-
self.analysis_function = analysis_function
|
| 558 |
-
self.model_info = model_info or {'available': False}
|
| 559 |
-
self.interface = None
|
| 560 |
-
|
| 561 |
-
def analyze_market(self, symbol, price, volume, sentiment_text):
|
| 562 |
-
"""Função principal de análise"""
|
| 563 |
-
try:
|
| 564 |
-
# Validar entrada
|
| 565 |
-
if not symbol or not price:
|
| 566 |
-
return "❌ Erro: Símbolo e preço são obrigatórios"
|
| 567 |
-
|
| 568 |
-
price = float(price)
|
| 569 |
-
volume = float(volume) if volume else 0
|
| 570 |
-
|
| 571 |
-
# Criar dados de mercado
|
| 572 |
-
market_data = {
|
| 573 |
-
'price': price,
|
| 574 |
-
'variation': 0, # Será calculado se necessário
|
| 575 |
-
'rsi': 50, # Valor padrão
|
| 576 |
-
'ema_trend': 'NEUTRO',
|
| 577 |
-
'bb_position': 'DENTRO',
|
| 578 |
-
'volume': volume
|
| 579 |
-
}
|
| 580 |
-
|
| 581 |
-
# Executar análise
|
| 582 |
-
result = self.analysis_function(f"{symbol}: {price}")
|
| 583 |
-
|
| 584 |
-
# Formatar resultado
|
| 585 |
-
return self.format_analysis_result(result, sentiment_text, symbol)
|
| 586 |
-
|
| 587 |
-
except Exception as e:
|
| 588 |
-
return f"❌ Erro na análise: {str(e)}"
|
| 589 |
-
|
| 590 |
-
def analyze_bot_log(self, log_content):
|
| 591 |
-
"""Função para analisar logs do bot externo"""
|
| 592 |
-
try:
|
| 593 |
-
if not log_content.strip():
|
| 594 |
-
return "❌ Erro: Conteúdo do log é obrigatório"
|
| 595 |
-
|
| 596 |
-
# Executar análise do log
|
| 597 |
-
result = self.analysis_function(log_content)
|
| 598 |
-
|
| 599 |
-
if 'error' in result:
|
| 600 |
-
return f"❌ {result['error']}"
|
| 601 |
-
|
| 602 |
-
# Formatar resultado específico do bot
|
| 603 |
-
return self.format_bot_analysis_result(result)
|
| 604 |
-
|
| 605 |
-
except Exception as e:
|
| 606 |
-
return f"❌ Erro na análise do log: {str(e)}"
|
| 607 |
-
|
| 608 |
-
def format_analysis_result(self, result, sentiment_text, symbol):
|
| 609 |
-
"""Formata resultado da análise"""
|
| 610 |
-
return ResultFormatter.format_main_result(result)
|
| 611 |
-
|
| 612 |
-
def format_bot_analysis_result(self, result):
|
| 613 |
-
"""Formata resultado específico da análise do bot"""
|
| 614 |
-
return ResultFormatter.format_main_result(result)
|
| 615 |
-
|
| 616 |
-
def create_interface(self) -> gr.Blocks:
|
| 617 |
-
"""Cria interface completa do Gradio."""
|
| 618 |
-
with gr.Blocks(
|
| 619 |
-
title=AppConfig.APP_TITLE,
|
| 620 |
-
theme=gr.themes.Soft(),
|
| 621 |
-
css=self._get_custom_css()
|
| 622 |
-
) as interface:
|
| 623 |
-
# Cabeçalho
|
| 624 |
-
gr.HTML(UIComponents.create_header())
|
| 625 |
-
|
| 626 |
-
# Layout principal
|
| 627 |
-
with gr.Row():
|
| 628 |
-
# Coluna de entrada (40%)
|
| 629 |
-
with gr.Column(scale=2):
|
| 630 |
-
input_section, market_input, analyze_btn, symbol_input, price_input, volume_input, sentiment_input, analyze_manual_btn = UIComponents.create_input_section()
|
| 631 |
-
|
| 632 |
-
# Coluna de saída (60%)
|
| 633 |
-
with gr.Column(scale=3):
|
| 634 |
-
output_section, outputs = UIComponents.create_output_section()
|
| 635 |
-
|
| 636 |
-
# Rodapé
|
| 637 |
-
gr.HTML(UIComponents.create_footer(self.model_info))
|
| 638 |
-
|
| 639 |
-
# Configurar eventos de análise
|
| 640 |
-
analyze_btn.click(
|
| 641 |
-
fn=self._analyze_wrapper,
|
| 642 |
-
inputs=[market_input],
|
| 643 |
-
outputs=[
|
| 644 |
-
outputs['ai_status'],
|
| 645 |
-
outputs['main_result'],
|
| 646 |
-
outputs['technical_analysis'],
|
| 647 |
-
outputs['sentiment_analysis'],
|
| 648 |
-
outputs['recommendations'],
|
| 649 |
-
outputs['harmonic_patterns'],
|
| 650 |
-
outputs['fibonacci_alerts'],
|
| 651 |
-
outputs['raw_data']
|
| 652 |
-
]
|
| 653 |
-
)
|
| 654 |
-
|
| 655 |
-
analyze_manual_btn.click(
|
| 656 |
-
fn=self._analyze_manual_wrapper,
|
| 657 |
-
inputs=[symbol_input, price_input, volume_input, sentiment_input],
|
| 658 |
-
outputs=[
|
| 659 |
-
outputs['ai_status'],
|
| 660 |
-
outputs['main_result'],
|
| 661 |
-
outputs['technical_analysis'],
|
| 662 |
-
outputs['sentiment_analysis'],
|
| 663 |
-
outputs['recommendations'],
|
| 664 |
-
outputs['harmonic_patterns'],
|
| 665 |
-
outputs['fibonacci_alerts'],
|
| 666 |
-
outputs['raw_data']
|
| 667 |
-
]
|
| 668 |
-
)
|
| 669 |
-
|
| 670 |
-
# Atualizar status da IA na inicialização
|
| 671 |
-
interface.load(
|
| 672 |
-
fn=lambda: UIComponents._get_ai_status_html(
|
| 673 |
-
self.model_info.get('available', False),
|
| 674 |
-
self.model_info.get('description', '')
|
| 675 |
-
),
|
| 676 |
-
outputs=[outputs['ai_status']]
|
| 677 |
-
)
|
| 678 |
-
|
| 679 |
-
self.interface = interface
|
| 680 |
-
return interface
|
| 681 |
-
|
| 682 |
-
def _analyze_wrapper(self, market_input: str) -> Tuple[str, str, str, str, str, str, str, Dict[str, Any]]:
|
| 683 |
-
"""Wrapper para função de análise com formatação de saída."""
|
| 684 |
-
try:
|
| 685 |
-
# Executar análise
|
| 686 |
-
analysis_result = self.analysis_function(market_input)
|
| 687 |
-
|
| 688 |
-
# Formatear resultados
|
| 689 |
-
ai_status = UIComponents._get_ai_status_html(
|
| 690 |
-
self.model_info.get('available', False),
|
| 691 |
-
self.model_info.get('description', '')
|
| 692 |
-
)
|
| 693 |
-
|
| 694 |
-
main_result = ResultFormatter.format_main_result(analysis_result)
|
| 695 |
-
technical_analysis = ResultFormatter.format_technical_analysis(analysis_result)
|
| 696 |
-
sentiment_analysis = ResultFormatter.format_sentiment_analysis(analysis_result)
|
| 697 |
-
recommendations = ResultFormatter.format_recommendations(analysis_result)
|
| 698 |
-
harmonic_patterns = ResultFormatter.format_harmonic_patterns(analysis_result)
|
| 699 |
-
fibonacci_alerts = ResultFormatter.format_fibonacci_alerts(analysis_result)
|
| 700 |
-
|
| 701 |
-
# Dados brutos para debug
|
| 702 |
-
raw_data = {
|
| 703 |
-
'timestamp': DateTimeUtils.get_current_datetime(),
|
| 704 |
-
'analysis_result': analysis_result
|
| 705 |
-
}
|
| 706 |
-
|
| 707 |
-
return (
|
| 708 |
-
ai_status,
|
| 709 |
-
main_result,
|
| 710 |
-
technical_analysis,
|
| 711 |
-
sentiment_analysis,
|
| 712 |
-
recommendations,
|
| 713 |
-
harmonic_patterns,
|
| 714 |
-
fibonacci_alerts,
|
| 715 |
-
raw_data
|
| 716 |
-
)
|
| 717 |
-
|
| 718 |
-
except Exception as e:
|
| 719 |
-
error_msg = f"Erro na análise: {str(e)}"
|
| 720 |
-
error_html = f"""
|
| 721 |
-
<div style="background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 8px; padding: 15px; color: #721c24;">
|
| 722 |
-
<h4 style="margin-top: 0;">❌ Erro na Análise</h4>
|
| 723 |
-
<p style="margin: 0;">{error_msg}</p>
|
| 724 |
-
</div>
|
| 725 |
-
"""
|
| 726 |
-
|
| 727 |
-
return (
|
| 728 |
-
UIComponents._get_ai_status_html(False),
|
| 729 |
-
error_html,
|
| 730 |
-
error_html,
|
| 731 |
-
error_html,
|
| 732 |
-
error_html,
|
| 733 |
-
error_html,
|
| 734 |
-
error_html,
|
| 735 |
-
{'error': error_msg}
|
| 736 |
-
)
|
| 737 |
-
|
| 738 |
-
def _analyze_manual_wrapper(self, symbol: str, price: float, volume: float, sentiment_text: str) -> Tuple[str, str, str, str, str, str, str, Dict[str, Any]]:
|
| 739 |
-
"""Wrapper para análise manual com formatação de saída."""
|
| 740 |
-
try:
|
| 741 |
-
# Validar entrada
|
| 742 |
-
if not symbol or not price:
|
| 743 |
-
raise ValueError("Símbolo e preço são obrigatórios")
|
| 744 |
-
|
| 745 |
-
# Criar entrada formatada
|
| 746 |
-
market_input = f"{symbol}: Preço={price}, Volume={volume or 0}"
|
| 747 |
-
if sentiment_text:
|
| 748 |
-
market_input += f", Sentimento={sentiment_text}"
|
| 749 |
-
|
| 750 |
-
# Executar análise usando o wrapper padrão
|
| 751 |
-
return self._analyze_wrapper(market_input)
|
| 752 |
-
|
| 753 |
-
except Exception as e:
|
| 754 |
-
error_msg = f"Erro na análise manual: {str(e)}"
|
| 755 |
-
error_html = f"""
|
| 756 |
-
<div style="background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 8px; padding: 15px; color: #721c24;">
|
| 757 |
-
<h4 style="margin-top: 0;">❌ Erro na Análise Manual</h4>
|
| 758 |
-
<p style="margin: 0;">{error_msg}</p>
|
| 759 |
-
</div>
|
| 760 |
-
"""
|
| 761 |
-
|
| 762 |
-
return (
|
| 763 |
-
UIComponents._get_ai_status_html(False),
|
| 764 |
-
error_html,
|
| 765 |
-
error_html,
|
| 766 |
-
error_html,
|
| 767 |
-
error_html,
|
| 768 |
-
error_html,
|
| 769 |
-
error_html,
|
| 770 |
-
{'error': error_msg}
|
| 771 |
-
)
|
| 772 |
-
|
| 773 |
-
def _get_custom_css(self) -> str:
|
| 774 |
-
"""Retorna CSS customizado para a interface."""
|
| 775 |
-
return """
|
| 776 |
-
.gradio-container {
|
| 777 |
-
max-width: 1200px !important;
|
| 778 |
-
margin: auto !important;
|
| 779 |
-
}
|
| 780 |
-
|
| 781 |
-
.gr-button {
|
| 782 |
-
transition: all 0.3s ease !important;
|
| 783 |
-
}
|
| 784 |
-
|
| 785 |
-
.gr-button:hover {
|
| 786 |
-
transform: translateY(-2px) !important;
|
| 787 |
-
box-shadow: 0 4px 8px rgba(0,0,0,0.2) !important;
|
| 788 |
-
}
|
| 789 |
-
|
| 790 |
-
.gr-textbox textarea {
|
| 791 |
-
font-family: 'Courier New', monospace !important;
|
| 792 |
-
}
|
| 793 |
-
|
| 794 |
-
.gr-tab-nav {
|
| 795 |
-
background: #f8f9fa !important;
|
| 796 |
-
}
|
| 797 |
-
|
| 798 |
-
.gr-tab-nav button {
|
| 799 |
-
border-radius: 8px 8px 0 0 !important;
|
| 800 |
-
}
|
| 801 |
-
"""
|
| 802 |
-
|
| 803 |
-
def launch(self, **kwargs) -> None:
|
| 804 |
-
"""Lança a interface."""
|
| 805 |
-
if not self.interface:
|
| 806 |
-
self.create_interface()
|
| 807 |
-
|
| 808 |
-
default_kwargs = {
|
| 809 |
-
'server_name': '127.0.0.1',
|
| 810 |
-
'server_port': 7860,
|
| 811 |
-
'share': False,
|
| 812 |
-
'show_error': True
|
| 813 |
-
}
|
| 814 |
-
|
| 815 |
-
# Mesclar argumentos padrão com os fornecidos
|
| 816 |
-
launch_kwargs = {**default_kwargs, **kwargs}
|
| 817 |
-
|
| 818 |
-
self.interface.launch(**launch_kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
utils.py
DELETED
|
@@ -1,330 +0,0 @@
|
|
| 1 |
-
"""Módulo de utilitários e funções auxiliares."""
|
| 2 |
-
|
| 3 |
-
import json
|
| 4 |
-
from datetime import datetime
|
| 5 |
-
from typing import Dict, Any, Optional
|
| 6 |
-
|
| 7 |
-
from config import (
|
| 8 |
-
TradingConfig,
|
| 9 |
-
UIConfig,
|
| 10 |
-
ScoringConfig
|
| 11 |
-
)
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class DateTimeUtils:
|
| 15 |
-
"""Utilitários para manipulação de data e hora."""
|
| 16 |
-
|
| 17 |
-
@staticmethod
|
| 18 |
-
def get_current_timestamp() -> str:
|
| 19 |
-
"""Retorna timestamp atual formatado."""
|
| 20 |
-
return datetime.now().strftime("%H:%M:%S")
|
| 21 |
-
|
| 22 |
-
@staticmethod
|
| 23 |
-
def get_current_datetime() -> str:
|
| 24 |
-
"""Retorna data e hora atual formatada."""
|
| 25 |
-
return datetime.now().strftime("%d/%m/%Y %H:%M:%S")
|
| 26 |
-
|
| 27 |
-
@staticmethod
|
| 28 |
-
def format_timestamp(dt: datetime) -> str:
|
| 29 |
-
"""Formata datetime para timestamp."""
|
| 30 |
-
return dt.strftime("%H:%M:%S")
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
class NumberUtils:
|
| 34 |
-
"""Utilitários para manipulação de números."""
|
| 35 |
-
|
| 36 |
-
@staticmethod
|
| 37 |
-
def format_price(price: float) -> str:
|
| 38 |
-
"""Formata preço com separadores de milhares."""
|
| 39 |
-
return f"{price:,.0f}"
|
| 40 |
-
|
| 41 |
-
@staticmethod
|
| 42 |
-
def format_percentage(value: float) -> str:
|
| 43 |
-
"""Formata porcentagem com sinal."""
|
| 44 |
-
return f"{value:+.2f}%"
|
| 45 |
-
|
| 46 |
-
@staticmethod
|
| 47 |
-
def format_volume(volume: float) -> str:
|
| 48 |
-
"""Formata volume com uma casa decimal."""
|
| 49 |
-
return f"{volume:.1f}x"
|
| 50 |
-
|
| 51 |
-
@staticmethod
|
| 52 |
-
def calculate_points_from_percentage(price: float, percentage: float) -> float:
|
| 53 |
-
"""Calcula pontos baseado em porcentagem do preço."""
|
| 54 |
-
return price * (percentage / 100)
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
class ConfidenceUtils:
|
| 58 |
-
"""Utilitários para manipulação de níveis de confiança."""
|
| 59 |
-
|
| 60 |
-
@staticmethod
|
| 61 |
-
def get_confidence_level(confidence: int) -> str:
|
| 62 |
-
"""Retorna nível de confiança textual."""
|
| 63 |
-
config = TradingConfig.CONFIDENCE_LEVELS
|
| 64 |
-
|
| 65 |
-
if confidence >= config['MUITO_ALTA']:
|
| 66 |
-
return "MUITO ALTA"
|
| 67 |
-
elif confidence >= config['ALTA']:
|
| 68 |
-
return "ALTA"
|
| 69 |
-
elif confidence >= config['MODERADA']:
|
| 70 |
-
return "MODERADA"
|
| 71 |
-
else:
|
| 72 |
-
return "BAIXA"
|
| 73 |
-
|
| 74 |
-
@staticmethod
|
| 75 |
-
def generate_confidence_bar(confidence: int) -> str:
|
| 76 |
-
"""Gera barra visual de confiança."""
|
| 77 |
-
filled_bars = int(confidence / 10)
|
| 78 |
-
empty_bars = 10 - filled_bars
|
| 79 |
-
return "█" * filled_bars + "░" * empty_bars
|
| 80 |
-
|
| 81 |
-
@staticmethod
|
| 82 |
-
def is_high_confidence(confidence: int) -> bool:
|
| 83 |
-
"""Verifica se confiança é alta."""
|
| 84 |
-
return confidence >= TradingConfig.CONFIDENCE_LEVELS['ALTA']
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
class ActionUtils:
|
| 88 |
-
"""Utilitários para manipulação de ações de trading."""
|
| 89 |
-
|
| 90 |
-
@staticmethod
|
| 91 |
-
def get_action_emojis(action: str) -> Dict[str, str]:
|
| 92 |
-
"""Retorna emojis para ação específica."""
|
| 93 |
-
return UIConfig.ACTION_EMOJIS.get(action, {
|
| 94 |
-
'main': '⚪',
|
| 95 |
-
'action': '❓'
|
| 96 |
-
})
|
| 97 |
-
|
| 98 |
-
@staticmethod
|
| 99 |
-
def get_action_color(action: str) -> str:
|
| 100 |
-
"""Retorna cor para ação específica."""
|
| 101 |
-
return UIConfig.ACTION_COLORS.get(action, 'cinza')
|
| 102 |
-
|
| 103 |
-
@staticmethod
|
| 104 |
-
def get_trading_direction(action: str) -> str:
|
| 105 |
-
"""Retorna direção de trading para ação."""
|
| 106 |
-
return UIConfig.TRADING_DIRECTIONS.get(action, 'INDEFINIDO')
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
class SentimentUtils:
|
| 110 |
-
"""Utilitários para manipulação de sentimento."""
|
| 111 |
-
|
| 112 |
-
@staticmethod
|
| 113 |
-
def get_sentiment_emoji(sentiment_label: str) -> str:
|
| 114 |
-
"""Retorna emoji para sentimento."""
|
| 115 |
-
return UIConfig.SENTIMENT_EMOJIS.get(sentiment_label, '😐💛')
|
| 116 |
-
|
| 117 |
-
@staticmethod
|
| 118 |
-
def normalize_sentiment_label(label: str) -> str:
|
| 119 |
-
"""Normaliza label de sentimento."""
|
| 120 |
-
label_upper = label.upper()
|
| 121 |
-
valid_labels = ['POSITIVO', 'NEGATIVO', 'NEUTRO']
|
| 122 |
-
|
| 123 |
-
if label_upper in valid_labels:
|
| 124 |
-
return label_upper
|
| 125 |
-
|
| 126 |
-
# Mapeamento de labels alternativos
|
| 127 |
-
label_mapping = {
|
| 128 |
-
'POSITIVE': 'POSITIVO',
|
| 129 |
-
'NEGATIVE': 'NEGATIVO',
|
| 130 |
-
'NEUTRAL': 'NEUTRO',
|
| 131 |
-
'POS': 'POSITIVO',
|
| 132 |
-
'NEG': 'NEGATIVO',
|
| 133 |
-
'NEU': 'NEUTRO'
|
| 134 |
-
}
|
| 135 |
-
|
| 136 |
-
return label_mapping.get(label_upper, 'NEUTRO')
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
class ValidationUtils:
|
| 140 |
-
"""Utilitários para validação de dados."""
|
| 141 |
-
|
| 142 |
-
@staticmethod
|
| 143 |
-
def validate_market_data(data: Dict[str, Any]) -> bool:
|
| 144 |
-
"""Valida dados de mercado."""
|
| 145 |
-
required_fields = ['price', 'variation', 'rsi', 'ema_trend', 'bb_position', 'volume']
|
| 146 |
-
|
| 147 |
-
# Verificar se todos os campos obrigatórios estão presentes
|
| 148 |
-
for field in required_fields:
|
| 149 |
-
if field not in data:
|
| 150 |
-
return False
|
| 151 |
-
|
| 152 |
-
# Validar tipos e valores
|
| 153 |
-
try:
|
| 154 |
-
price = float(data['price'])
|
| 155 |
-
variation = float(data['variation'])
|
| 156 |
-
rsi = int(data['rsi'])
|
| 157 |
-
volume = float(data['volume'])
|
| 158 |
-
|
| 159 |
-
# Validar ranges
|
| 160 |
-
if price < 0 or not (0 <= rsi <= 100) or volume < 0:
|
| 161 |
-
return False
|
| 162 |
-
|
| 163 |
-
# Validar strings
|
| 164 |
-
valid_ema_trends = ['ALTA', 'BAIXA', 'NEUTRO']
|
| 165 |
-
valid_bb_positions = ['DENTRO', 'SOBRE', 'ABAIXO', 'ACIMA']
|
| 166 |
-
|
| 167 |
-
if (data['ema_trend'] not in valid_ema_trends or
|
| 168 |
-
data['bb_position'] not in valid_bb_positions):
|
| 169 |
-
return False
|
| 170 |
-
|
| 171 |
-
return True
|
| 172 |
-
|
| 173 |
-
except (ValueError, TypeError):
|
| 174 |
-
return False
|
| 175 |
-
|
| 176 |
-
@staticmethod
|
| 177 |
-
def validate_confidence_score(score: int) -> int:
|
| 178 |
-
"""Valida e normaliza pontuação de confiança."""
|
| 179 |
-
return max(ScoringConfig.MIN_CONFIDENCE,
|
| 180 |
-
min(ScoringConfig.MAX_CONFIDENCE, score))
|
| 181 |
-
|
| 182 |
-
@staticmethod
|
| 183 |
-
def validate_text_input(text: str) -> bool:
|
| 184 |
-
"""Valida entrada de texto."""
|
| 185 |
-
if not text or not isinstance(text, str):
|
| 186 |
-
return False
|
| 187 |
-
|
| 188 |
-
# Verificar se não é apenas espaços em branco
|
| 189 |
-
if not text.strip():
|
| 190 |
-
return False
|
| 191 |
-
|
| 192 |
-
# Verificar tamanho mínimo
|
| 193 |
-
if len(text.strip()) < 3:
|
| 194 |
-
return False
|
| 195 |
-
|
| 196 |
-
return True
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
class FormatUtils:
|
| 200 |
-
"""Utilitários para formatação de texto e dados."""
|
| 201 |
-
|
| 202 |
-
@staticmethod
|
| 203 |
-
def format_signal_list(signals: list) -> str:
|
| 204 |
-
"""Formata lista de sinais para exibição."""
|
| 205 |
-
if not signals:
|
| 206 |
-
return "Nenhum sinal detectado"
|
| 207 |
-
|
| 208 |
-
formatted_signals = []
|
| 209 |
-
for i, signal in enumerate(signals[:5], 1): # Máximo 5 sinais
|
| 210 |
-
if hasattr(signal, 'description'):
|
| 211 |
-
formatted_signals.append(f"{i}. {signal.description}")
|
| 212 |
-
else:
|
| 213 |
-
formatted_signals.append(f"{i}. {str(signal)}")
|
| 214 |
-
|
| 215 |
-
return "\n".join(formatted_signals)
|
| 216 |
-
|
| 217 |
-
@staticmethod
|
| 218 |
-
def format_market_summary(market_data: Dict[str, Any]) -> str:
|
| 219 |
-
"""Formata resumo dos dados de mercado."""
|
| 220 |
-
price = NumberUtils.format_price(market_data.get('price', 0))
|
| 221 |
-
variation = NumberUtils.format_percentage(market_data.get('variation', 0))
|
| 222 |
-
volume = NumberUtils.format_volume(market_data.get('volume', 0))
|
| 223 |
-
|
| 224 |
-
return f"""• **Preço:** {price}
|
| 225 |
-
• **Variação:** {variation}
|
| 226 |
-
• **RSI:** {market_data.get('rsi', 'N/A')}
|
| 227 |
-
• **EMA:** {market_data.get('ema_trend', 'N/A')}
|
| 228 |
-
• **Bollinger:** {market_data.get('bb_position', 'N/A')}
|
| 229 |
-
• **Volume:** {volume}"""
|
| 230 |
-
|
| 231 |
-
@staticmethod
|
| 232 |
-
def format_trading_recommendations(action: str, price: float) -> str:
|
| 233 |
-
"""Formata recomendações de trading."""
|
| 234 |
-
if action == 'COMPRAR':
|
| 235 |
-
stop_loss = price * (1 - TradingConfig.STOP_LOSS_PERCENTAGE)
|
| 236 |
-
take_profit = price * (1 + TradingConfig.TAKE_PROFIT_PERCENTAGE)
|
| 237 |
-
|
| 238 |
-
return f"""• **Stop Loss:** -{NumberUtils.calculate_points_from_percentage(price, TradingConfig.STOP_LOSS_PERCENTAGE * 100):.0f} pts ({TradingConfig.STOP_LOSS_PERCENTAGE * 100:.2f}%)
|
| 239 |
-
• **Take Profit:** +{NumberUtils.calculate_points_from_percentage(price, TradingConfig.TAKE_PROFIT_PERCENTAGE * 100):.0f} pts ({TradingConfig.TAKE_PROFIT_PERCENTAGE * 100:.2f}%)
|
| 240 |
-
• **Timeframe:** {'/'.join(TradingConfig.SCALPING_TIMEFRAMES)}
|
| 241 |
-
• **Risk/Reward:** 1:{TradingConfig.RISK_REWARD_RATIO}"""
|
| 242 |
-
|
| 243 |
-
elif action == 'VENDER':
|
| 244 |
-
stop_loss = price * (1 + TradingConfig.STOP_LOSS_PERCENTAGE)
|
| 245 |
-
take_profit = price * (1 - TradingConfig.TAKE_PROFIT_PERCENTAGE)
|
| 246 |
-
|
| 247 |
-
return f"""• **Stop Loss:** +{NumberUtils.calculate_points_from_percentage(price, TradingConfig.STOP_LOSS_PERCENTAGE * 100):.0f} pts ({TradingConfig.STOP_LOSS_PERCENTAGE * 100:.2f}%)
|
| 248 |
-
• **Take Profit:** -{NumberUtils.calculate_points_from_percentage(price, TradingConfig.TAKE_PROFIT_PERCENTAGE * 100):.0f} pts ({TradingConfig.TAKE_PROFIT_PERCENTAGE * 100:.2f}%)
|
| 249 |
-
• **Timeframe:** {'/'.join(TradingConfig.SCALPING_TIMEFRAMES)}
|
| 250 |
-
• **Risk/Reward:** 1:{TradingConfig.RISK_REWARD_RATIO}"""
|
| 251 |
-
|
| 252 |
-
else:
|
| 253 |
-
return """• **Aguardar:** Setup mais definido
|
| 254 |
-
• **Monitorar:** Rompimentos de suporte/resistência
|
| 255 |
-
• **Observar:** Confluência de sinais técnicos"""
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
class LogUtils:
|
| 259 |
-
"""Utilitários para logging e debug."""
|
| 260 |
-
|
| 261 |
-
@staticmethod
|
| 262 |
-
def log_analysis_result(analysis_result: Dict[str, Any]) -> None:
|
| 263 |
-
"""Registra resultado de análise para debug."""
|
| 264 |
-
timestamp = DateTimeUtils.get_current_datetime()
|
| 265 |
-
action = analysis_result.get('action', 'UNKNOWN')
|
| 266 |
-
confidence = analysis_result.get('confidence', 0)
|
| 267 |
-
|
| 268 |
-
print(f"[{timestamp}] Análise: {action} (Confiança: {confidence}%)")
|
| 269 |
-
|
| 270 |
-
@staticmethod
|
| 271 |
-
def log_error(error_message: str, context: str = "") -> None:
|
| 272 |
-
"""Registra erro com contexto."""
|
| 273 |
-
timestamp = DateTimeUtils.get_current_datetime()
|
| 274 |
-
context_str = f" [{context}]" if context else ""
|
| 275 |
-
print(f"[{timestamp}] ERRO{context_str}: {error_message}")
|
| 276 |
-
|
| 277 |
-
@staticmethod
|
| 278 |
-
def log_model_status(model_info: Dict[str, Any]) -> None:
|
| 279 |
-
"""Registra status do modelo de IA."""
|
| 280 |
-
timestamp = DateTimeUtils.get_current_datetime()
|
| 281 |
-
status = "ATIVO" if model_info.get('available', False) else "INATIVO"
|
| 282 |
-
model_name = model_info.get('description', 'Desconhecido')
|
| 283 |
-
|
| 284 |
-
print(f"[{timestamp}] Modelo IA: {status} - {model_name}")
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
class DataExportUtils:
|
| 288 |
-
"""Utilitários para exportação de dados."""
|
| 289 |
-
|
| 290 |
-
@staticmethod
|
| 291 |
-
def export_analysis_to_json(analysis_result: Dict[str, Any]) -> str:
|
| 292 |
-
"""Exporta resultado de análise para JSON."""
|
| 293 |
-
# Preparar dados para serialização
|
| 294 |
-
export_data = {
|
| 295 |
-
'timestamp': DateTimeUtils.get_current_datetime(),
|
| 296 |
-
'action': analysis_result.get('action'),
|
| 297 |
-
'confidence': analysis_result.get('confidence'),
|
| 298 |
-
'market_data': analysis_result.get('market_data'),
|
| 299 |
-
'sentiment': analysis_result.get('sentiment')
|
| 300 |
-
}
|
| 301 |
-
|
| 302 |
-
# Converter objetos complexos para dicionários
|
| 303 |
-
if 'signals' in analysis_result:
|
| 304 |
-
export_data['signals'] = [
|
| 305 |
-
{
|
| 306 |
-
'indicator': getattr(signal, 'indicator', 'unknown'),
|
| 307 |
-
'signal_type': getattr(signal, 'signal_type', 'unknown'),
|
| 308 |
-
'strength': getattr(signal, 'strength', 0),
|
| 309 |
-
'description': getattr(signal, 'description', '')
|
| 310 |
-
}
|
| 311 |
-
for signal in analysis_result['signals']
|
| 312 |
-
]
|
| 313 |
-
|
| 314 |
-
return json.dumps(export_data, indent=2, ensure_ascii=False)
|
| 315 |
-
|
| 316 |
-
@staticmethod
|
| 317 |
-
def create_analysis_summary(analysis_result: Dict[str, Any]) -> Dict[str, Any]:
|
| 318 |
-
"""Cria resumo da análise para relatórios."""
|
| 319 |
-
return {
|
| 320 |
-
'timestamp': DateTimeUtils.get_current_datetime(),
|
| 321 |
-
'action': analysis_result.get('action', 'UNKNOWN'),
|
| 322 |
-
'confidence': analysis_result.get('confidence', 0),
|
| 323 |
-
'confidence_level': ConfidenceUtils.get_confidence_level(
|
| 324 |
-
analysis_result.get('confidence', 0)
|
| 325 |
-
),
|
| 326 |
-
'signals_count': len(analysis_result.get('signals', [])),
|
| 327 |
-
'sentiment_label': analysis_result.get('sentiment', {}).get('label', 'NEUTRO'),
|
| 328 |
-
'market_price': analysis_result.get('market_data', {}).get('price', 0),
|
| 329 |
-
'market_rsi': analysis_result.get('market_data', {}).get('rsi', 50)
|
| 330 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|