Spaces:
Sleeping
Sleeping
Initial commit: Complete Fibonacci analysis application with Gradio interface
Browse files- __init__.py +1 -0
- __pycache__/market_analysis.cpython-313.pyc +0 -0
- advanced_market_processing.py +466 -0
- app.py +566 -16
- config/__init__.py +25 -0
- config/__pycache__/__init__.cpython-313.pyc +0 -0
- config/__pycache__/config.cpython-313.pyc +0 -0
- config/config.py +209 -0
- docs/README.md +86 -0
- docs/api-reference.md +569 -0
- docs/architecture.md +350 -0
- docs/configuration.md +606 -0
- docs/developer-guide.md +963 -0
- docs/installation.md +216 -0
- docs/troubleshooting.md +679 -0
- fibonacci_analysis.py +528 -0
- log_parser.py +335 -0
- real_time_integration.py +358 -0
- src/__init__.py +0 -0
- src/__pycache__/__init__.cpython-313.pyc +0 -0
- src/analysis/__init__.py +0 -0
- src/analysis/__pycache__/__init__.cpython-313.pyc +0 -0
- src/analysis/__pycache__/market_analysis.cpython-313.pyc +0 -0
- src/analysis/fibonacci_analysis.py +528 -0
- market_analysis.py → src/analysis/market_analysis.py +144 -3
- src/analysis/sentiment_analysis.py +331 -0
- src/core/__init__.py +0 -0
- src/core/log_parser.py +335 -0
- src/core/performance_monitor.py +366 -0
- src/integrations/__init__.py +0 -0
- src/integrations/real_time_integration.py +358 -0
- src/ui/__init__.py +0 -0
- src/ui/__pycache__/gradio_interface.cpython-313.pyc +0 -0
- src/ui/gradio_interface.py +828 -0
- src/utils/__init__.py +0 -0
- src/utils/__pycache__/__init__.cpython-313.pyc +0 -0
- src/utils/__pycache__/utils.cpython-313.pyc +0 -0
- src/utils/utils.py +330 -0
- tests/__init__.py +0 -0
- tests/integration/__init__.py +0 -0
- tests/unit/__init__.py +0 -0
- text +76 -0
- ui.py +359 -17
__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Arquivo __init__.py para tornar o diretório raiz um pacote Python
|
__pycache__/market_analysis.cpython-313.pyc
CHANGED
|
Binary files a/__pycache__/market_analysis.cpython-313.pyc and b/__pycache__/market_analysis.cpython-313.pyc differ
|
|
|
advanced_market_processing.py
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pandas as pd
|
| 3 |
+
from typing import Dict, List, Optional, Tuple, Any
|
| 4 |
+
from dataclasses import dataclass
|
| 5 |
+
import logging
|
| 6 |
+
from datetime import datetime
|
| 7 |
+
|
| 8 |
+
# Configurar logging
|
| 9 |
+
logging.basicConfig(level=logging.INFO)
|
| 10 |
+
logger = logging.getLogger(__name__)
|
| 11 |
+
|
| 12 |
+
@dataclass
|
| 13 |
+
class SwingPoint:
|
| 14 |
+
"""Representa um ponto de swing no mercado"""
|
| 15 |
+
price: float
|
| 16 |
+
timestamp: datetime
|
| 17 |
+
type: str # 'high' ou 'low'
|
| 18 |
+
strength: float # 0-1, força do swing
|
| 19 |
+
volume: Optional[float] = None
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class ConfluenceZone:
|
| 23 |
+
"""Representa uma zona de confluência"""
|
| 24 |
+
price_level: float
|
| 25 |
+
strength: float # 0-1
|
| 26 |
+
factors: List[str] # Fatores que contribuem para a confluência
|
| 27 |
+
type: str # 'support' ou 'resistance'
|
| 28 |
+
reliability: float # 0-1
|
| 29 |
+
|
| 30 |
+
@dataclass
|
| 31 |
+
class HarmonicPattern:
|
| 32 |
+
"""Representa um padrão harmônico"""
|
| 33 |
+
name: str # 'Gartley', 'Butterfly', 'Bat', 'Crab'
|
| 34 |
+
points: Dict[str, SwingPoint] # X, A, B, C, D
|
| 35 |
+
completion_level: float
|
| 36 |
+
target_levels: List[float]
|
| 37 |
+
stop_level: float
|
| 38 |
+
reliability: float # 0-1
|
| 39 |
+
|
| 40 |
+
class SwingPointDetector:
|
| 41 |
+
"""Detector de pontos de swing"""
|
| 42 |
+
|
| 43 |
+
def __init__(self, lookback_period: int = 5, min_strength: float = 0.3):
|
| 44 |
+
self.lookback_period = lookback_period
|
| 45 |
+
self.min_strength = min_strength
|
| 46 |
+
|
| 47 |
+
def detect_swing_points(self, prices: np.ndarray, volumes: Optional[np.ndarray] = None) -> List[SwingPoint]:
|
| 48 |
+
"""Detecta pontos de swing em uma série de preços"""
|
| 49 |
+
try:
|
| 50 |
+
swing_points = []
|
| 51 |
+
|
| 52 |
+
for i in range(self.lookback_period, len(prices) - self.lookback_period):
|
| 53 |
+
# Verificar swing high
|
| 54 |
+
if self._is_swing_high(prices, i):
|
| 55 |
+
strength = self._calculate_swing_strength(prices, i, 'high')
|
| 56 |
+
if strength >= self.min_strength:
|
| 57 |
+
swing_points.append(SwingPoint(
|
| 58 |
+
price=prices[i],
|
| 59 |
+
timestamp=datetime.now(),
|
| 60 |
+
type='high',
|
| 61 |
+
strength=strength,
|
| 62 |
+
volume=volumes[i] if volumes is not None else None
|
| 63 |
+
))
|
| 64 |
+
|
| 65 |
+
# Verificar swing low
|
| 66 |
+
elif self._is_swing_low(prices, i):
|
| 67 |
+
strength = self._calculate_swing_strength(prices, i, 'low')
|
| 68 |
+
if strength >= self.min_strength:
|
| 69 |
+
swing_points.append(SwingPoint(
|
| 70 |
+
price=prices[i],
|
| 71 |
+
timestamp=datetime.now(),
|
| 72 |
+
type='low',
|
| 73 |
+
strength=strength,
|
| 74 |
+
volume=volumes[i] if volumes is not None else None
|
| 75 |
+
))
|
| 76 |
+
|
| 77 |
+
return swing_points
|
| 78 |
+
|
| 79 |
+
except Exception as e:
|
| 80 |
+
logger.error(f"Erro ao detectar swing points: {e}")
|
| 81 |
+
return []
|
| 82 |
+
|
| 83 |
+
def _is_swing_high(self, prices: np.ndarray, index: int) -> bool:
|
| 84 |
+
"""Verifica se um ponto é um swing high"""
|
| 85 |
+
current_price = prices[index]
|
| 86 |
+
left_prices = prices[index - self.lookback_period:index]
|
| 87 |
+
right_prices = prices[index + 1:index + self.lookback_period + 1]
|
| 88 |
+
|
| 89 |
+
return (current_price > np.max(left_prices) and
|
| 90 |
+
current_price > np.max(right_prices))
|
| 91 |
+
|
| 92 |
+
def _is_swing_low(self, prices: np.ndarray, index: int) -> bool:
|
| 93 |
+
"""Verifica se um ponto é um swing low"""
|
| 94 |
+
current_price = prices[index]
|
| 95 |
+
left_prices = prices[index - self.lookback_period:index]
|
| 96 |
+
right_prices = prices[index + 1:index + self.lookback_period + 1]
|
| 97 |
+
|
| 98 |
+
return (current_price < np.min(left_prices) and
|
| 99 |
+
current_price < np.min(right_prices))
|
| 100 |
+
|
| 101 |
+
def _calculate_swing_strength(self, prices: np.ndarray, index: int, swing_type: str) -> float:
|
| 102 |
+
"""Calcula a força de um swing point"""
|
| 103 |
+
try:
|
| 104 |
+
current_price = prices[index]
|
| 105 |
+
surrounding_prices = np.concatenate([
|
| 106 |
+
prices[max(0, index - self.lookback_period * 2):index],
|
| 107 |
+
prices[index + 1:min(len(prices), index + self.lookback_period * 2 + 1)]
|
| 108 |
+
])
|
| 109 |
+
|
| 110 |
+
if swing_type == 'high':
|
| 111 |
+
price_diff = current_price - np.mean(surrounding_prices)
|
| 112 |
+
max_diff = np.max(surrounding_prices) - np.min(surrounding_prices)
|
| 113 |
+
else:
|
| 114 |
+
price_diff = np.mean(surrounding_prices) - current_price
|
| 115 |
+
max_diff = np.max(surrounding_prices) - np.min(surrounding_prices)
|
| 116 |
+
|
| 117 |
+
return min(1.0, max(0.0, price_diff / max_diff)) if max_diff > 0 else 0.0
|
| 118 |
+
|
| 119 |
+
except Exception:
|
| 120 |
+
return 0.0
|
| 121 |
+
|
| 122 |
+
class ConfluenceZoneAnalyzer:
|
| 123 |
+
"""Analisador de zonas de confluência"""
|
| 124 |
+
|
| 125 |
+
def __init__(self, tolerance: float = 0.001):
|
| 126 |
+
self.tolerance = tolerance # Tolerância para agrupar níveis próximos
|
| 127 |
+
|
| 128 |
+
def analyze_confluence_zones(self,
|
| 129 |
+
swing_points: List[SwingPoint],
|
| 130 |
+
fibonacci_levels: Dict[str, float],
|
| 131 |
+
support_resistance_levels: List[float]) -> List[ConfluenceZone]:
|
| 132 |
+
"""Analisa zonas de confluência baseado em múltiplos fatores"""
|
| 133 |
+
try:
|
| 134 |
+
confluence_zones = []
|
| 135 |
+
all_levels = []
|
| 136 |
+
|
| 137 |
+
# Coletar todos os níveis relevantes
|
| 138 |
+
for swing in swing_points:
|
| 139 |
+
all_levels.append(('swing', swing.price, swing.strength))
|
| 140 |
+
|
| 141 |
+
for fib_name, fib_level in fibonacci_levels.items():
|
| 142 |
+
all_levels.append(('fibonacci', fib_level, 0.7))
|
| 143 |
+
|
| 144 |
+
for sr_level in support_resistance_levels:
|
| 145 |
+
all_levels.append(('support_resistance', sr_level, 0.8))
|
| 146 |
+
|
| 147 |
+
# Agrupar níveis próximos
|
| 148 |
+
grouped_levels = self._group_nearby_levels(all_levels)
|
| 149 |
+
|
| 150 |
+
# Criar zonas de confluência
|
| 151 |
+
for group in grouped_levels:
|
| 152 |
+
if len(group) >= 2: # Pelo menos 2 fatores para confluência
|
| 153 |
+
avg_price = np.mean([level[1] for level in group])
|
| 154 |
+
factors = [level[0] for level in group]
|
| 155 |
+
strength = np.mean([level[2] for level in group])
|
| 156 |
+
|
| 157 |
+
# Determinar tipo (support/resistance)
|
| 158 |
+
zone_type = self._determine_zone_type(group, swing_points)
|
| 159 |
+
|
| 160 |
+
confluence_zones.append(ConfluenceZone(
|
| 161 |
+
price_level=avg_price,
|
| 162 |
+
strength=strength,
|
| 163 |
+
factors=factors,
|
| 164 |
+
type=zone_type,
|
| 165 |
+
reliability=min(1.0, len(group) * 0.3)
|
| 166 |
+
))
|
| 167 |
+
|
| 168 |
+
return sorted(confluence_zones, key=lambda x: x.strength, reverse=True)
|
| 169 |
+
|
| 170 |
+
except Exception as e:
|
| 171 |
+
logger.error(f"Erro ao analisar zonas de confluência: {e}")
|
| 172 |
+
return []
|
| 173 |
+
|
| 174 |
+
def _group_nearby_levels(self, levels: List[Tuple]) -> List[List[Tuple]]:
|
| 175 |
+
"""Agrupa níveis próximos baseado na tolerância"""
|
| 176 |
+
if not levels:
|
| 177 |
+
return []
|
| 178 |
+
|
| 179 |
+
sorted_levels = sorted(levels, key=lambda x: x[1])
|
| 180 |
+
groups = []
|
| 181 |
+
current_group = [sorted_levels[0]]
|
| 182 |
+
|
| 183 |
+
for level in sorted_levels[1:]:
|
| 184 |
+
if abs(level[1] - current_group[-1][1]) / current_group[-1][1] <= self.tolerance:
|
| 185 |
+
current_group.append(level)
|
| 186 |
+
else:
|
| 187 |
+
groups.append(current_group)
|
| 188 |
+
current_group = [level]
|
| 189 |
+
|
| 190 |
+
groups.append(current_group)
|
| 191 |
+
return groups
|
| 192 |
+
|
| 193 |
+
def _determine_zone_type(self, group: List[Tuple], swing_points: List[SwingPoint]) -> str:
|
| 194 |
+
"""Determina se a zona é de suporte ou resistência"""
|
| 195 |
+
# Lógica simplificada - pode ser melhorada
|
| 196 |
+
swing_highs = [s for s in swing_points if s.type == 'high']
|
| 197 |
+
swing_lows = [s for s in swing_points if s.type == 'low']
|
| 198 |
+
|
| 199 |
+
avg_price = np.mean([level[1] for level in group])
|
| 200 |
+
|
| 201 |
+
if swing_lows:
|
| 202 |
+
avg_low = np.mean([s.price for s in swing_lows])
|
| 203 |
+
if abs(avg_price - avg_low) < abs(avg_price - np.mean([s.price for s in swing_highs]) if swing_highs else avg_price):
|
| 204 |
+
return 'support'
|
| 205 |
+
|
| 206 |
+
return 'resistance'
|
| 207 |
+
|
| 208 |
+
class HarmonicPatternDetector:
|
| 209 |
+
"""Detector de padrões harmônicos"""
|
| 210 |
+
|
| 211 |
+
def __init__(self):
|
| 212 |
+
self.pattern_ratios = {
|
| 213 |
+
'Gartley': {
|
| 214 |
+
'XA_AB': (0.618, 0.618),
|
| 215 |
+
'AB_BC': (0.382, 0.886),
|
| 216 |
+
'BC_CD': (1.13, 1.618),
|
| 217 |
+
'XA_AD': (0.786, 0.786)
|
| 218 |
+
},
|
| 219 |
+
'Butterfly': {
|
| 220 |
+
'XA_AB': (0.786, 0.786),
|
| 221 |
+
'AB_BC': (0.382, 0.886),
|
| 222 |
+
'BC_CD': (1.618, 2.618),
|
| 223 |
+
'XA_AD': (1.27, 1.618)
|
| 224 |
+
},
|
| 225 |
+
'Bat': {
|
| 226 |
+
'XA_AB': (0.382, 0.5),
|
| 227 |
+
'AB_BC': (0.382, 0.886),
|
| 228 |
+
'BC_CD': (1.618, 2.618),
|
| 229 |
+
'XA_AD': (0.886, 0.886)
|
| 230 |
+
},
|
| 231 |
+
'Crab': {
|
| 232 |
+
'XA_AB': (0.382, 0.618),
|
| 233 |
+
'AB_BC': (0.382, 0.886),
|
| 234 |
+
'BC_CD': (2.24, 3.618),
|
| 235 |
+
'XA_AD': (1.618, 1.618)
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
def detect_harmonic_patterns(self, swing_points: List[SwingPoint]) -> List[HarmonicPattern]:
|
| 240 |
+
"""Detecta padrões harmônicos nos swing points"""
|
| 241 |
+
try:
|
| 242 |
+
patterns = []
|
| 243 |
+
|
| 244 |
+
if len(swing_points) < 5:
|
| 245 |
+
return patterns
|
| 246 |
+
|
| 247 |
+
# Procurar por sequências de 5 pontos (X, A, B, C, D)
|
| 248 |
+
for i in range(len(swing_points) - 4):
|
| 249 |
+
points = swing_points[i:i+5]
|
| 250 |
+
|
| 251 |
+
# Verificar se a sequência alterna entre high/low
|
| 252 |
+
if self._is_valid_sequence(points):
|
| 253 |
+
for pattern_name, ratios in self.pattern_ratios.items():
|
| 254 |
+
pattern = self._check_pattern(points, pattern_name, ratios)
|
| 255 |
+
if pattern:
|
| 256 |
+
patterns.append(pattern)
|
| 257 |
+
|
| 258 |
+
return patterns
|
| 259 |
+
|
| 260 |
+
except Exception as e:
|
| 261 |
+
logger.error(f"Erro ao detectar padrões harmônicos: {e}")
|
| 262 |
+
return []
|
| 263 |
+
|
| 264 |
+
def _is_valid_sequence(self, points: List[SwingPoint]) -> bool:
|
| 265 |
+
"""Verifica se a sequência de pontos é válida para padrões harmônicos"""
|
| 266 |
+
types = [p.type for p in points]
|
| 267 |
+
|
| 268 |
+
# Deve alternar entre high e low
|
| 269 |
+
for i in range(1, len(types)):
|
| 270 |
+
if types[i] == types[i-1]:
|
| 271 |
+
return False
|
| 272 |
+
|
| 273 |
+
return True
|
| 274 |
+
|
| 275 |
+
def _check_pattern(self, points: List[SwingPoint], pattern_name: str, ratios: Dict) -> Optional[HarmonicPattern]:
|
| 276 |
+
"""Verifica se os pontos formam um padrão harmônico específico"""
|
| 277 |
+
try:
|
| 278 |
+
X, A, B, C, D = points
|
| 279 |
+
|
| 280 |
+
# Calcular distâncias
|
| 281 |
+
XA = abs(A.price - X.price)
|
| 282 |
+
AB = abs(B.price - A.price)
|
| 283 |
+
BC = abs(C.price - B.price)
|
| 284 |
+
CD = abs(D.price - C.price)
|
| 285 |
+
XD = abs(D.price - X.price)
|
| 286 |
+
|
| 287 |
+
# Verificar ratios
|
| 288 |
+
tolerance = 0.05 # 5% de tolerância
|
| 289 |
+
|
| 290 |
+
ratios_to_check = [
|
| 291 |
+
('XA_AB', AB / XA if XA != 0 else 0),
|
| 292 |
+
('AB_BC', BC / AB if AB != 0 else 0),
|
| 293 |
+
('BC_CD', CD / BC if BC != 0 else 0),
|
| 294 |
+
('XA_AD', XD / XA if XA != 0 else 0)
|
| 295 |
+
]
|
| 296 |
+
|
| 297 |
+
valid_ratios = 0
|
| 298 |
+
for ratio_name, calculated_ratio in ratios_to_check:
|
| 299 |
+
expected_min, expected_max = ratios[ratio_name]
|
| 300 |
+
if expected_min * (1 - tolerance) <= calculated_ratio <= expected_max * (1 + tolerance):
|
| 301 |
+
valid_ratios += 1
|
| 302 |
+
|
| 303 |
+
# Padrão válido se pelo menos 3 dos 4 ratios estiverem corretos
|
| 304 |
+
if valid_ratios >= 3:
|
| 305 |
+
# Calcular níveis de target e stop
|
| 306 |
+
target_levels = self._calculate_targets(X, A, B, C, D)
|
| 307 |
+
stop_level = self._calculate_stop_level(X, A, B, C, D)
|
| 308 |
+
|
| 309 |
+
return HarmonicPattern(
|
| 310 |
+
name=pattern_name,
|
| 311 |
+
points={'X': X, 'A': A, 'B': B, 'C': C, 'D': D},
|
| 312 |
+
completion_level=D.price,
|
| 313 |
+
target_levels=target_levels,
|
| 314 |
+
stop_level=stop_level,
|
| 315 |
+
reliability=valid_ratios / 4.0
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
return None
|
| 319 |
+
|
| 320 |
+
except Exception as e:
|
| 321 |
+
logger.error(f"Erro ao verificar padrão {pattern_name}: {e}")
|
| 322 |
+
return None
|
| 323 |
+
|
| 324 |
+
def _calculate_targets(self, X, A, B, C, D) -> List[float]:
|
| 325 |
+
"""Calcula níveis de target para o padrão"""
|
| 326 |
+
try:
|
| 327 |
+
XA = abs(A.price - X.price)
|
| 328 |
+
|
| 329 |
+
# Targets baseados em retracements de Fibonacci
|
| 330 |
+
if D.type == 'low': # Padrão bullish
|
| 331 |
+
target1 = D.price + (XA * 0.382)
|
| 332 |
+
target2 = D.price + (XA * 0.618)
|
| 333 |
+
target3 = D.price + XA
|
| 334 |
+
else: # Padrão bearish
|
| 335 |
+
target1 = D.price - (XA * 0.382)
|
| 336 |
+
target2 = D.price - (XA * 0.618)
|
| 337 |
+
target3 = D.price - XA
|
| 338 |
+
|
| 339 |
+
return [target1, target2, target3]
|
| 340 |
+
|
| 341 |
+
except Exception:
|
| 342 |
+
return [D.price]
|
| 343 |
+
|
| 344 |
+
def _calculate_stop_level(self, X, A, B, C, D) -> float:
|
| 345 |
+
"""Calcula nível de stop loss para o padrão"""
|
| 346 |
+
try:
|
| 347 |
+
# Stop além do ponto D
|
| 348 |
+
if D.type == 'low': # Padrão bullish
|
| 349 |
+
return D.price * 0.99 # 1% abaixo
|
| 350 |
+
else: # Padrão bearish
|
| 351 |
+
return D.price * 1.01 # 1% acima
|
| 352 |
+
except Exception:
|
| 353 |
+
return D.price
|
| 354 |
+
|
| 355 |
+
class AdvancedMarketProcessor:
|
| 356 |
+
"""Processador avançado de dados de mercado"""
|
| 357 |
+
|
| 358 |
+
def __init__(self):
|
| 359 |
+
self.swing_detector = SwingPointDetector()
|
| 360 |
+
self.confluence_analyzer = ConfluenceZoneAnalyzer()
|
| 361 |
+
self.harmonic_detector = HarmonicPatternDetector()
|
| 362 |
+
logger.info("AdvancedMarketProcessor inicializado")
|
| 363 |
+
|
| 364 |
+
def process_market_data(self,
|
| 365 |
+
prices: np.ndarray,
|
| 366 |
+
volumes: Optional[np.ndarray] = None,
|
| 367 |
+
fibonacci_levels: Optional[Dict[str, float]] = None,
|
| 368 |
+
support_resistance_levels: Optional[List[float]] = None) -> Dict[str, Any]:
|
| 369 |
+
"""Processa dados de mercado com análise avançada"""
|
| 370 |
+
try:
|
| 371 |
+
# Detectar swing points
|
| 372 |
+
swing_points = self.swing_detector.detect_swing_points(prices, volumes)
|
| 373 |
+
|
| 374 |
+
# Analisar zonas de confluência
|
| 375 |
+
confluence_zones = []
|
| 376 |
+
if fibonacci_levels or support_resistance_levels:
|
| 377 |
+
confluence_zones = self.confluence_analyzer.analyze_confluence_zones(
|
| 378 |
+
swing_points,
|
| 379 |
+
fibonacci_levels or {},
|
| 380 |
+
support_resistance_levels or []
|
| 381 |
+
)
|
| 382 |
+
|
| 383 |
+
# Detectar padrões harmônicos
|
| 384 |
+
harmonic_patterns = self.harmonic_detector.detect_harmonic_patterns(swing_points)
|
| 385 |
+
|
| 386 |
+
# Compilar resultado
|
| 387 |
+
result = {
|
| 388 |
+
'swing_points': {
|
| 389 |
+
'count': len(swing_points),
|
| 390 |
+
'highs': [sp for sp in swing_points if sp.type == 'high'],
|
| 391 |
+
'lows': [sp for sp in swing_points if sp.type == 'low'],
|
| 392 |
+
'avg_strength': np.mean([sp.strength for sp in swing_points]) if swing_points else 0
|
| 393 |
+
},
|
| 394 |
+
'confluence_zones': {
|
| 395 |
+
'count': len(confluence_zones),
|
| 396 |
+
'zones': confluence_zones,
|
| 397 |
+
'strongest_zone': max(confluence_zones, key=lambda x: x.strength) if confluence_zones else None
|
| 398 |
+
},
|
| 399 |
+
'harmonic_patterns': {
|
| 400 |
+
'count': len(harmonic_patterns),
|
| 401 |
+
'patterns': harmonic_patterns,
|
| 402 |
+
'most_reliable': max(harmonic_patterns, key=lambda x: x.reliability) if harmonic_patterns else None
|
| 403 |
+
},
|
| 404 |
+
'market_structure': self._analyze_market_structure(swing_points),
|
| 405 |
+
'key_levels': self._identify_key_levels(swing_points, confluence_zones)
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
return result
|
| 409 |
+
|
| 410 |
+
except Exception as e:
|
| 411 |
+
logger.error(f"Erro no processamento avançado de mercado: {e}")
|
| 412 |
+
return {
|
| 413 |
+
'swing_points': {'count': 0, 'highs': [], 'lows': [], 'avg_strength': 0},
|
| 414 |
+
'confluence_zones': {'count': 0, 'zones': [], 'strongest_zone': None},
|
| 415 |
+
'harmonic_patterns': {'count': 0, 'patterns': [], 'most_reliable': None},
|
| 416 |
+
'market_structure': 'UNKNOWN',
|
| 417 |
+
'key_levels': []
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
def _analyze_market_structure(self, swing_points: List[SwingPoint]) -> str:
|
| 421 |
+
"""Analisa a estrutura do mercado baseado nos swing points"""
|
| 422 |
+
try:
|
| 423 |
+
if len(swing_points) < 4:
|
| 424 |
+
return 'INSUFFICIENT_DATA'
|
| 425 |
+
|
| 426 |
+
recent_points = swing_points[-4:]
|
| 427 |
+
highs = [sp for sp in recent_points if sp.type == 'high']
|
| 428 |
+
lows = [sp for sp in recent_points if sp.type == 'low']
|
| 429 |
+
|
| 430 |
+
if len(highs) >= 2 and len(lows) >= 2:
|
| 431 |
+
# Verificar tendência dos highs e lows
|
| 432 |
+
high_trend = 'UP' if highs[-1].price > highs[0].price else 'DOWN'
|
| 433 |
+
low_trend = 'UP' if lows[-1].price > lows[0].price else 'DOWN'
|
| 434 |
+
|
| 435 |
+
if high_trend == 'UP' and low_trend == 'UP':
|
| 436 |
+
return 'UPTREND'
|
| 437 |
+
elif high_trend == 'DOWN' and low_trend == 'DOWN':
|
| 438 |
+
return 'DOWNTREND'
|
| 439 |
+
else:
|
| 440 |
+
return 'SIDEWAYS'
|
| 441 |
+
|
| 442 |
+
return 'CONSOLIDATION'
|
| 443 |
+
|
| 444 |
+
except Exception:
|
| 445 |
+
return 'UNKNOWN'
|
| 446 |
+
|
| 447 |
+
def _identify_key_levels(self, swing_points: List[SwingPoint], confluence_zones: List[ConfluenceZone]) -> List[float]:
|
| 448 |
+
"""Identifica níveis-chave baseado em swing points e confluências"""
|
| 449 |
+
try:
|
| 450 |
+
key_levels = []
|
| 451 |
+
|
| 452 |
+
# Adicionar swing points mais fortes
|
| 453 |
+
strong_swings = [sp for sp in swing_points if sp.strength > 0.7]
|
| 454 |
+
key_levels.extend([sp.price for sp in strong_swings])
|
| 455 |
+
|
| 456 |
+
# Adicionar zonas de confluência mais fortes
|
| 457 |
+
strong_zones = [cz for cz in confluence_zones if cz.strength > 0.6]
|
| 458 |
+
key_levels.extend([cz.price_level for cz in strong_zones])
|
| 459 |
+
|
| 460 |
+
# Remover duplicatas e ordenar
|
| 461 |
+
key_levels = sorted(list(set(key_levels)))
|
| 462 |
+
|
| 463 |
+
return key_levels[:10] # Retornar apenas os 10 mais importantes
|
| 464 |
+
|
| 465 |
+
except Exception:
|
| 466 |
+
return []
|
app.py
CHANGED
|
@@ -6,11 +6,15 @@ from datetime import datetime
|
|
| 6 |
|
| 7 |
# Importar módulos refatorados
|
| 8 |
try:
|
| 9 |
-
from config import AppConfig
|
| 10 |
-
from market_analysis import TechnicalAnalysisEngine
|
| 11 |
-
from sentiment_analysis import SentimentAnalysisEngine
|
| 12 |
-
from ui import GradioInterface
|
| 13 |
-
from utils import LogUtils, ValidationUtils
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
except ImportError:
|
| 15 |
# Fallback para modo standalone se módulos não existirem
|
| 16 |
print("⚠️ Módulos refatorados não encontrados. Executando em modo standalone.")
|
|
@@ -20,12 +24,27 @@ except ImportError:
|
|
| 20 |
GradioInterface = None
|
| 21 |
LogUtils = None
|
| 22 |
ValidationUtils = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
# Engines de análise
|
| 25 |
technical_engine = None
|
| 26 |
sentiment_engine = None
|
|
|
|
| 27 |
model_info = {'available': False, 'description': 'IA Indisponível'}
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
# Inicializar engines
|
| 30 |
def initialize_engines():
|
| 31 |
"""Inicializa engines de análise técnica e de sentimento."""
|
|
@@ -483,6 +502,15 @@ def process_trading_analysis(text):
|
|
| 483 |
return "⚠️ **Erro:** Nenhum dado de mercado fornecido para análise."
|
| 484 |
|
| 485 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
# Parse dos dados de mercado
|
| 487 |
market_data = parse_market_data(text)
|
| 488 |
|
|
@@ -506,6 +534,50 @@ def process_trading_analysis(text):
|
|
| 506 |
|
| 507 |
return f"❌ **Erro:** {error_msg}"
|
| 508 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 509 |
# Função principal de análise para a interface
|
| 510 |
def main_analysis_function(text: str) -> str:
|
| 511 |
"""Função principal de análise que será usada pela interface."""
|
|
@@ -552,26 +624,504 @@ Notícias: Mercado otimista com dados do PIB"""
|
|
| 552 |
</div>
|
| 553 |
""")
|
| 554 |
|
| 555 |
-
with gr.
|
| 556 |
-
with gr.
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 566 |
|
| 567 |
analyze_btn.click(
|
| 568 |
fn=main_analysis_function,
|
| 569 |
inputs=[market_input],
|
| 570 |
outputs=[result_output]
|
| 571 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 572 |
|
| 573 |
return interface
|
| 574 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 575 |
# Inicializar engines e criar interface
|
| 576 |
initialize_engines()
|
| 577 |
demo = create_interface()
|
|
|
|
| 6 |
|
| 7 |
# Importar módulos refatorados
|
| 8 |
try:
|
| 9 |
+
from config.config import AppConfig
|
| 10 |
+
from src.analysis.market_analysis import TechnicalAnalysisEngine
|
| 11 |
+
from src.analysis.sentiment_analysis import SentimentAnalysisEngine
|
| 12 |
+
from src.ui.gradio_interface import GradioInterface
|
| 13 |
+
from src.utils.utils import LogUtils, ValidationUtils
|
| 14 |
+
from src.core.log_parser import VampireBotLogParser
|
| 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.")
|
|
|
|
| 24 |
GradioInterface = None
|
| 25 |
LogUtils = None
|
| 26 |
ValidationUtils = None
|
| 27 |
+
VampireBotLogParser = None
|
| 28 |
+
AdvancedFibonacciEngine = None
|
| 29 |
+
AdvancedMarketProcessor = None
|
| 30 |
+
RealTimeIntegration = None
|
| 31 |
+
BotEvent = None
|
| 32 |
+
PerformanceMonitor = None
|
| 33 |
+
measure_analysis_time = None
|
| 34 |
|
| 35 |
# Engines de análise
|
| 36 |
technical_engine = None
|
| 37 |
sentiment_engine = None
|
| 38 |
+
real_time_integration = None
|
| 39 |
model_info = {'available': False, 'description': 'IA Indisponível'}
|
| 40 |
|
| 41 |
+
# Estado global para eventos em tempo real
|
| 42 |
+
recent_events = []
|
| 43 |
+
max_events_display = 50
|
| 44 |
+
|
| 45 |
+
# Monitor de performance global
|
| 46 |
+
performance_monitor = PerformanceMonitor() if PerformanceMonitor else None
|
| 47 |
+
|
| 48 |
# Inicializar engines
|
| 49 |
def initialize_engines():
|
| 50 |
"""Inicializa engines de análise técnica e de sentimento."""
|
|
|
|
| 502 |
return "⚠️ **Erro:** Nenhum dado de mercado fornecido para análise."
|
| 503 |
|
| 504 |
try:
|
| 505 |
+
# Verificar se é um log do bot
|
| 506 |
+
if 'VAMPIRE TRADING BOT' in text or 'FIBONACCI AVANÇADO' in text:
|
| 507 |
+
if VampireBotLogParser:
|
| 508 |
+
log_parser = VampireBotLogParser()
|
| 509 |
+
return log_parser.process_bot_log(text)
|
| 510 |
+
else:
|
| 511 |
+
# Fallback para processamento de log
|
| 512 |
+
return process_bot_log_fallback(text)
|
| 513 |
+
|
| 514 |
# Parse dos dados de mercado
|
| 515 |
market_data = parse_market_data(text)
|
| 516 |
|
|
|
|
| 534 |
|
| 535 |
return f"❌ **Erro:** {error_msg}"
|
| 536 |
|
| 537 |
+
def process_bot_log_fallback(text):
|
| 538 |
+
"""Processamento fallback para logs do bot quando VampireBotLogParser não está disponível."""
|
| 539 |
+
try:
|
| 540 |
+
# Extrair informações básicas do log
|
| 541 |
+
import re
|
| 542 |
+
|
| 543 |
+
# Buscar por padrões específicos do bot
|
| 544 |
+
fibonacci_match = re.search(r'FIBONACCI AVANÇADO.*?Nível: ([\d.]+)', text, re.DOTALL)
|
| 545 |
+
action_match = re.search(r'(COMPRAR|VENDER|AGUARDAR)', text)
|
| 546 |
+
confidence_match = re.search(r'Confiança: (\d+)%', text)
|
| 547 |
+
|
| 548 |
+
if fibonacci_match or action_match:
|
| 549 |
+
response = "🤖 **ANÁLISE DE LOG DO BOT DETECTADA**\n\n"
|
| 550 |
+
|
| 551 |
+
if action_match:
|
| 552 |
+
action = action_match.group(1)
|
| 553 |
+
response += f"**Ação Recomendada:** {action}\n"
|
| 554 |
+
|
| 555 |
+
if confidence_match:
|
| 556 |
+
confidence = confidence_match.group(1)
|
| 557 |
+
response += f"**Confiança:** {confidence}%\n"
|
| 558 |
+
|
| 559 |
+
if fibonacci_match:
|
| 560 |
+
fib_level = fibonacci_match.group(1)
|
| 561 |
+
response += f"**Nível Fibonacci:** {fib_level}\n"
|
| 562 |
+
|
| 563 |
+
response += "\n⚠️ **Nota:** Processamento básico de log. Para análise completa, instale os módulos avançados."
|
| 564 |
+
return response
|
| 565 |
+
|
| 566 |
+
# Se não conseguir extrair dados do log, processar como entrada normal
|
| 567 |
+
return process_trading_analysis_basic(text)
|
| 568 |
+
|
| 569 |
+
except Exception as e:
|
| 570 |
+
return f"❌ **Erro no processamento do log:** {str(e)}"
|
| 571 |
+
|
| 572 |
+
def process_trading_analysis_basic(text):
|
| 573 |
+
"""Processamento básico quando não há log do bot."""
|
| 574 |
+
market_data = parse_market_data(text)
|
| 575 |
+
if not market_data:
|
| 576 |
+
return "⚠️ **Erro:** Não foi possível extrair dados válidos do texto fornecido."
|
| 577 |
+
|
| 578 |
+
analysis = analyze_scalping_signals(market_data, 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."""
|
|
|
|
| 624 |
</div>
|
| 625 |
""")
|
| 626 |
|
| 627 |
+
with gr.Tab("📊 Análise de Mercado"):
|
| 628 |
+
with gr.Row():
|
| 629 |
+
with gr.Column(scale=2):
|
| 630 |
+
market_input = gr.Textbox(
|
| 631 |
+
label="Dados do Mercado",
|
| 632 |
+
placeholder=example_data,
|
| 633 |
+
lines=8
|
| 634 |
+
)
|
| 635 |
+
analyze_btn = gr.Button("🔍 Analisar", variant="primary")
|
| 636 |
+
|
| 637 |
+
with gr.Column(scale=3):
|
| 638 |
+
result_output = gr.HTML()
|
| 639 |
+
|
| 640 |
+
with gr.Tab("🤖 Monitor do Bot"):
|
| 641 |
+
with gr.Row():
|
| 642 |
+
with gr.Column(scale=1):
|
| 643 |
+
gr.Markdown("### 🔄 Status do Sistema")
|
| 644 |
+
|
| 645 |
+
with gr.Row():
|
| 646 |
+
start_monitor_btn = gr.Button("▶️ Iniciar Monitor", variant="primary")
|
| 647 |
+
stop_monitor_btn = gr.Button("⏹️ Parar Monitor", variant="secondary")
|
| 648 |
+
|
| 649 |
+
monitor_status = gr.HTML(
|
| 650 |
+
value="<div style='color: #666;'>Monitor parado</div>"
|
| 651 |
+
)
|
| 652 |
+
|
| 653 |
+
gr.Markdown("### ⚡ Eventos Recentes")
|
| 654 |
+
recent_events_display = gr.HTML(
|
| 655 |
+
value="<div style='color: #666;'>Nenhum evento recente</div>"
|
| 656 |
+
)
|
| 657 |
+
|
| 658 |
+
with gr.Column(scale=2):
|
| 659 |
+
gr.Markdown("### 📊 Análise em Tempo Real")
|
| 660 |
+
real_time_analysis = gr.HTML(
|
| 661 |
+
value="<div style='text-align: center; color: #666; padding: 40px;'>Aguardando dados do bot...</div>"
|
| 662 |
+
)
|
| 663 |
+
|
| 664 |
+
log_file_path = gr.Textbox(
|
| 665 |
+
value="d:/hugging_face_spaces/text",
|
| 666 |
+
label="📁 Caminho do Arquivo de Log"
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
+
check_interval = gr.Slider(
|
| 670 |
+
minimum=0.5,
|
| 671 |
+
maximum=10.0,
|
| 672 |
+
value=1.0,
|
| 673 |
+
step=0.5,
|
| 674 |
+
label="⏱️ Intervalo de Verificação (segundos)"
|
| 675 |
+
)
|
| 676 |
+
|
| 677 |
+
with gr.Tab("📈 Performance"):
|
| 678 |
+
with gr.Row():
|
| 679 |
+
with gr.Column(scale=1):
|
| 680 |
+
gr.Markdown("### 🔧 Controles de Performance")
|
| 681 |
+
|
| 682 |
+
with gr.Row():
|
| 683 |
+
start_perf_btn = gr.Button("▶️ Iniciar Monitoramento", variant="primary")
|
| 684 |
+
stop_perf_btn = gr.Button("⏹️ Parar Monitoramento", variant="secondary")
|
| 685 |
+
|
| 686 |
+
perf_status = gr.HTML(
|
| 687 |
+
value="<div style='color: #666;'>Monitoramento parado</div>"
|
| 688 |
+
)
|
| 689 |
+
|
| 690 |
+
with gr.Row():
|
| 691 |
+
reset_stats_btn = gr.Button("🔄 Reset Estatísticas", variant="secondary")
|
| 692 |
+
export_metrics_btn = gr.Button("💾 Exportar Métricas", variant="secondary")
|
| 693 |
+
|
| 694 |
+
gr.Markdown("### 🚨 Alertas de Performance")
|
| 695 |
+
performance_alerts = gr.HTML(
|
| 696 |
+
value="<div style='color: #666;'>Nenhum alerta ativo</div>"
|
| 697 |
+
)
|
| 698 |
+
|
| 699 |
+
with gr.Column(scale=2):
|
| 700 |
+
gr.Markdown("### 📊 Métricas do Sistema")
|
| 701 |
+
system_metrics = gr.HTML(
|
| 702 |
+
value="<div style='text-align: center; color: #666; padding: 40px;'>Aguardando métricas...</div>"
|
| 703 |
+
)
|
| 704 |
+
|
| 705 |
+
gr.Markdown("### 🤖 Estatísticas do Bot")
|
| 706 |
+
bot_statistics = gr.HTML(
|
| 707 |
+
value="<div style='text-align: center; color: #666; padding: 40px;'>Aguardando estatísticas...</div>"
|
| 708 |
+
)
|
| 709 |
+
|
| 710 |
+
gr.Markdown("### 💡 Sugestões de Otimização")
|
| 711 |
+
optimization_suggestions = gr.HTML(
|
| 712 |
+
value="<div style='color: #666;'>Execute análises para obter sugestões</div>"
|
| 713 |
+
)
|
| 714 |
|
| 715 |
analyze_btn.click(
|
| 716 |
fn=main_analysis_function,
|
| 717 |
inputs=[market_input],
|
| 718 |
outputs=[result_output]
|
| 719 |
)
|
| 720 |
+
|
| 721 |
+
start_monitor_btn.click(
|
| 722 |
+
fn=start_real_time_monitor,
|
| 723 |
+
inputs=[log_file_path, check_interval],
|
| 724 |
+
outputs=[monitor_status]
|
| 725 |
+
)
|
| 726 |
+
|
| 727 |
+
stop_monitor_btn.click(
|
| 728 |
+
fn=stop_real_time_monitor,
|
| 729 |
+
outputs=[monitor_status]
|
| 730 |
+
)
|
| 731 |
+
|
| 732 |
+
# Event handlers para performance
|
| 733 |
+
start_perf_btn.click(
|
| 734 |
+
fn=start_performance_monitoring,
|
| 735 |
+
outputs=[perf_status]
|
| 736 |
+
)
|
| 737 |
+
|
| 738 |
+
stop_perf_btn.click(
|
| 739 |
+
fn=stop_performance_monitoring,
|
| 740 |
+
outputs=[perf_status]
|
| 741 |
+
)
|
| 742 |
+
|
| 743 |
+
reset_stats_btn.click(
|
| 744 |
+
fn=reset_performance_stats,
|
| 745 |
+
outputs=[bot_statistics]
|
| 746 |
+
)
|
| 747 |
+
|
| 748 |
+
export_metrics_btn.click(
|
| 749 |
+
fn=export_performance_metrics,
|
| 750 |
+
outputs=[perf_status]
|
| 751 |
+
)
|
| 752 |
+
|
| 753 |
+
# Auto-refresh para eventos em tempo real
|
| 754 |
+
interface.load(
|
| 755 |
+
fn=get_recent_events_display,
|
| 756 |
+
outputs=[recent_events_display]
|
| 757 |
+
)
|
| 758 |
+
|
| 759 |
+
interface.load(
|
| 760 |
+
fn=get_real_time_analysis_display,
|
| 761 |
+
outputs=[real_time_analysis]
|
| 762 |
+
)
|
| 763 |
+
|
| 764 |
+
# Auto-refresh para métricas de performance
|
| 765 |
+
interface.load(
|
| 766 |
+
fn=get_system_metrics_display,
|
| 767 |
+
outputs=[system_metrics]
|
| 768 |
+
)
|
| 769 |
+
|
| 770 |
+
interface.load(
|
| 771 |
+
fn=get_bot_statistics_display,
|
| 772 |
+
outputs=[bot_statistics]
|
| 773 |
+
)
|
| 774 |
+
|
| 775 |
+
interface.load(
|
| 776 |
+
fn=get_performance_alerts_display,
|
| 777 |
+
outputs=[performance_alerts]
|
| 778 |
+
)
|
| 779 |
+
|
| 780 |
+
interface.load(
|
| 781 |
+
fn=get_optimization_suggestions_display,
|
| 782 |
+
outputs=[optimization_suggestions]
|
| 783 |
+
)
|
| 784 |
|
| 785 |
return interface
|
| 786 |
|
| 787 |
+
# Funções para integração em tempo real
|
| 788 |
+
def start_real_time_monitor(log_path: str, interval: float):
|
| 789 |
+
"""Inicia o monitor em tempo real."""
|
| 790 |
+
global real_time_integration, recent_events
|
| 791 |
+
|
| 792 |
+
try:
|
| 793 |
+
if RealTimeIntegration:
|
| 794 |
+
real_time_integration = RealTimeIntegration(log_path)
|
| 795 |
+
real_time_integration.config.check_interval = interval
|
| 796 |
+
|
| 797 |
+
# Callback para capturar eventos
|
| 798 |
+
def event_handler(event):
|
| 799 |
+
global recent_events
|
| 800 |
+
event_dict = {
|
| 801 |
+
'timestamp': event.timestamp.strftime('%H:%M:%S'),
|
| 802 |
+
'type': event.event_type,
|
| 803 |
+
'data': event.data,
|
| 804 |
+
'priority': event.priority
|
| 805 |
+
}
|
| 806 |
+
recent_events.append(event_dict)
|
| 807 |
+
|
| 808 |
+
# Manter apenas os últimos eventos
|
| 809 |
+
if len(recent_events) > max_events_display:
|
| 810 |
+
recent_events = recent_events[-max_events_display:]
|
| 811 |
+
|
| 812 |
+
real_time_integration.processor.subscribe(event_handler)
|
| 813 |
+
real_time_integration.start()
|
| 814 |
+
return "<div style='color: green;'>✅ Monitor iniciado com sucesso</div>"
|
| 815 |
+
else:
|
| 816 |
+
return "<div style='color: orange;'>⚠️ Integração em tempo real não disponível</div>"
|
| 817 |
+
except Exception as e:
|
| 818 |
+
return f"<div style='color: red;'>❌ Erro ao iniciar monitor: {str(e)}</div>"
|
| 819 |
+
|
| 820 |
+
def stop_real_time_monitor():
|
| 821 |
+
"""Para o monitor em tempo real."""
|
| 822 |
+
global real_time_integration
|
| 823 |
+
|
| 824 |
+
try:
|
| 825 |
+
if real_time_integration:
|
| 826 |
+
real_time_integration.stop()
|
| 827 |
+
real_time_integration = None
|
| 828 |
+
return "<div style='color: orange;'>⏹️ Monitor parado</div>"
|
| 829 |
+
else:
|
| 830 |
+
return "<div style='color: #666;'>Monitor já estava parado</div>"
|
| 831 |
+
except Exception as e:
|
| 832 |
+
return f"<div style='color: red;'>❌ Erro ao parar monitor: {str(e)}</div>"
|
| 833 |
+
|
| 834 |
+
def get_recent_events_display():
|
| 835 |
+
"""Obtém display dos eventos recentes."""
|
| 836 |
+
global recent_events
|
| 837 |
+
|
| 838 |
+
if not recent_events:
|
| 839 |
+
return "<div style='color: #666;'>Nenhum evento recente</div>"
|
| 840 |
+
|
| 841 |
+
html = "<div style='max-height: 300px; overflow-y: auto;'>"
|
| 842 |
+
for event in recent_events[-10:]: # Últimos 10 eventos
|
| 843 |
+
timestamp = event.get('timestamp', 'N/A')
|
| 844 |
+
event_type = event.get('type', 'UNKNOWN')
|
| 845 |
+
data = event.get('data', {})
|
| 846 |
+
|
| 847 |
+
color = {
|
| 848 |
+
'COMPRAR': 'green',
|
| 849 |
+
'VENDER': 'red',
|
| 850 |
+
'AGUARDAR': 'orange'
|
| 851 |
+
}.get(event_type, '#666')
|
| 852 |
+
|
| 853 |
+
html += f"<div style='border-left: 3px solid {color}; padding: 5px; margin: 5px 0; background: #f9f9f9;'>"
|
| 854 |
+
html += f"<strong>{timestamp}</strong> - {event_type}<br>"
|
| 855 |
+
if 'action' in data:
|
| 856 |
+
html += f"Ação: {data['action']}<br>"
|
| 857 |
+
if 'confidence' in data:
|
| 858 |
+
html += f"Confiança: {data['confidence']}%"
|
| 859 |
+
html += "</div>"
|
| 860 |
+
|
| 861 |
+
html += "</div>"
|
| 862 |
+
return html
|
| 863 |
+
|
| 864 |
+
def get_real_time_analysis_display():
|
| 865 |
+
"""Obtém display da análise em tempo real."""
|
| 866 |
+
global real_time_integration, recent_events
|
| 867 |
+
|
| 868 |
+
if not real_time_integration or not recent_events:
|
| 869 |
+
return "<div style='text-align: center; color: #666; padding: 40px;'>Aguardando dados do bot...</div>"
|
| 870 |
+
|
| 871 |
+
# Pegar o evento mais recente
|
| 872 |
+
latest_event = recent_events[-1] if recent_events else None
|
| 873 |
+
|
| 874 |
+
if not latest_event:
|
| 875 |
+
return "<div style='text-align: center; color: #666; padding: 40px;'>Nenhum evento disponível</div>"
|
| 876 |
+
|
| 877 |
+
# Processar o evento mais recente
|
| 878 |
+
try:
|
| 879 |
+
event_data = latest_event.get('data', {})
|
| 880 |
+
if 'raw_text' in event_data:
|
| 881 |
+
analysis_result = main_analysis_function(event_data['raw_text'])
|
| 882 |
+
return f"<div style='border: 2px solid #4CAF50; padding: 15px; border-radius: 10px;'>{analysis_result}</div>"
|
| 883 |
+
else:
|
| 884 |
+
return "<div style='color: #666;'>Dados insuficientes para análise</div>"
|
| 885 |
+
except Exception as e:
|
| 886 |
+
return f"<div style='color: red;'>Erro na análise: {str(e)}</div>"
|
| 887 |
+
|
| 888 |
+
def get_system_stats():
|
| 889 |
+
"""Obtém estatísticas do sistema."""
|
| 890 |
+
global recent_events, real_time_integration
|
| 891 |
+
|
| 892 |
+
total_events = len(recent_events)
|
| 893 |
+
monitor_status = "Ativo" if real_time_integration else "Inativo"
|
| 894 |
+
|
| 895 |
+
# Contar tipos de eventos
|
| 896 |
+
event_counts = {}
|
| 897 |
+
for event in recent_events:
|
| 898 |
+
event_type = event.get('type', 'UNKNOWN')
|
| 899 |
+
event_counts[event_type] = event_counts.get(event_type, 0) + 1
|
| 900 |
+
|
| 901 |
+
html = f"""
|
| 902 |
+
<div style='padding: 20px;'>
|
| 903 |
+
<h3>📊 Estatísticas do Sistema</h3>
|
| 904 |
+
<p><strong>Status do Monitor:</strong> {monitor_status}</p>
|
| 905 |
+
<p><strong>Total de Eventos:</strong> {total_events}</p>
|
| 906 |
+
<p><strong>Modelo IA:</strong> {model_info.get('description', 'N/A')}</p>
|
| 907 |
+
|
| 908 |
+
<h4>📈 Distribuição de Eventos:</h4>
|
| 909 |
+
<ul>
|
| 910 |
+
"""
|
| 911 |
+
|
| 912 |
+
for event_type, count in event_counts.items():
|
| 913 |
+
html += f"<li>{event_type}: {count}</li>"
|
| 914 |
+
|
| 915 |
+
html += "</ul></div>"
|
| 916 |
+
return html
|
| 917 |
+
|
| 918 |
+
# Funções de controle de performance
|
| 919 |
+
def start_performance_monitoring():
|
| 920 |
+
"""Inicia o monitoramento de performance."""
|
| 921 |
+
global performance_monitor
|
| 922 |
+
|
| 923 |
+
if not performance_monitor:
|
| 924 |
+
return "<div style='color: red;'>❌ Monitor de performance não disponível no modo standalone</div>"
|
| 925 |
+
|
| 926 |
+
try:
|
| 927 |
+
if not performance_monitor.monitoring:
|
| 928 |
+
performance_monitor.start_monitoring(interval=5.0)
|
| 929 |
+
return "<div style='color: green;'>✅ Monitoramento de performance iniciado</div>"
|
| 930 |
+
else:
|
| 931 |
+
return "<div style='color: orange;'>⚠️ Monitoramento já está ativo</div>"
|
| 932 |
+
except Exception as e:
|
| 933 |
+
return f"<div style='color: red;'>❌ Erro ao iniciar monitoramento: {str(e)}</div>"
|
| 934 |
+
|
| 935 |
+
def stop_performance_monitoring():
|
| 936 |
+
"""Para o monitoramento de performance."""
|
| 937 |
+
global performance_monitor
|
| 938 |
+
|
| 939 |
+
if not performance_monitor:
|
| 940 |
+
return "<div style='color: red;'>❌ Monitor de performance não disponível no modo standalone</div>"
|
| 941 |
+
|
| 942 |
+
try:
|
| 943 |
+
if performance_monitor.monitoring:
|
| 944 |
+
performance_monitor.stop_monitoring()
|
| 945 |
+
return "<div style='color: orange;'>⏹️ Monitoramento de performance parado</div>"
|
| 946 |
+
else:
|
| 947 |
+
return "<div style='color: #666;'>Monitoramento já estava parado</div>"
|
| 948 |
+
except Exception as e:
|
| 949 |
+
return f"<div style='color: red;'>❌ Erro ao parar monitoramento: {str(e)}</div>"
|
| 950 |
+
|
| 951 |
+
def reset_performance_stats():
|
| 952 |
+
"""Reseta as estatísticas de performance."""
|
| 953 |
+
global performance_monitor
|
| 954 |
+
|
| 955 |
+
if not performance_monitor:
|
| 956 |
+
return "<div style='color: red;'>❌ Monitor de performance não disponível no modo standalone</div>"
|
| 957 |
+
|
| 958 |
+
try:
|
| 959 |
+
performance_monitor.reset_stats()
|
| 960 |
+
return "<div style='color: green;'>✅ Estatísticas resetadas com sucesso</div>"
|
| 961 |
+
except Exception as e:
|
| 962 |
+
return f"<div style='color: red;'>❌ Erro ao resetar estatísticas: {str(e)}</div>"
|
| 963 |
+
|
| 964 |
+
def export_performance_metrics():
|
| 965 |
+
"""Exporta métricas de performance."""
|
| 966 |
+
global performance_monitor
|
| 967 |
+
|
| 968 |
+
if not performance_monitor:
|
| 969 |
+
return "<div style='color: red;'>❌ Monitor de performance não disponível no modo standalone</div>"
|
| 970 |
+
|
| 971 |
+
try:
|
| 972 |
+
from datetime import datetime
|
| 973 |
+
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 974 |
+
filepath = f"d:/hugging_face_spaces/performance_metrics_{timestamp}.json"
|
| 975 |
+
|
| 976 |
+
performance_monitor.export_metrics(filepath, hours=24)
|
| 977 |
+
return f"<div style='color: green;'>✅ Métricas exportadas para: {filepath}</div>"
|
| 978 |
+
except Exception as e:
|
| 979 |
+
return f"<div style='color: red;'>❌ Erro ao exportar métricas: {str(e)}</div>"
|
| 980 |
+
|
| 981 |
+
def get_system_metrics_display():
|
| 982 |
+
"""Obtém display das métricas do sistema."""
|
| 983 |
+
global performance_monitor
|
| 984 |
+
|
| 985 |
+
if not performance_monitor:
|
| 986 |
+
return "<div style='color: #666;'>Monitor de performance não disponível no modo standalone</div>"
|
| 987 |
+
|
| 988 |
+
try:
|
| 989 |
+
current_metrics = performance_monitor.get_current_metrics()
|
| 990 |
+
|
| 991 |
+
if not current_metrics:
|
| 992 |
+
return "<div style='color: #666;'>Nenhuma métrica disponível</div>"
|
| 993 |
+
|
| 994 |
+
html = f"""
|
| 995 |
+
<div style='padding: 15px; border: 1px solid #ddd; border-radius: 8px;'>
|
| 996 |
+
<h4>🖥️ Métricas Atuais</h4>
|
| 997 |
+
<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 10px;'>
|
| 998 |
+
<div>
|
| 999 |
+
<strong>CPU:</strong> {current_metrics.cpu_usage:.1f}%<br>
|
| 1000 |
+
<div style='background: #f0f0f0; height: 10px; border-radius: 5px; margin: 5px 0;'>
|
| 1001 |
+
<div style='background: {'red' if current_metrics.cpu_usage > 80 else 'orange' if current_metrics.cpu_usage > 60 else 'green'};
|
| 1002 |
+
height: 100%; width: {current_metrics.cpu_usage}%; border-radius: 5px;'></div>
|
| 1003 |
+
</div>
|
| 1004 |
+
</div>
|
| 1005 |
+
<div>
|
| 1006 |
+
<strong>Memória:</strong> {current_metrics.memory_usage:.1f}%<br>
|
| 1007 |
+
<div style='background: #f0f0f0; height: 10px; border-radius: 5px; margin: 5px 0;'>
|
| 1008 |
+
<div style='background: {'red' if current_metrics.memory_usage > 85 else 'orange' if current_metrics.memory_usage > 70 else 'green'};
|
| 1009 |
+
height: 100%; width: {current_metrics.memory_usage}%; border-radius: 5px;'></div>
|
| 1010 |
+
</div>
|
| 1011 |
+
</div>
|
| 1012 |
+
</div>
|
| 1013 |
+
<p><strong>Memória Disponível:</strong> {current_metrics.memory_available:.2f} GB</p>
|
| 1014 |
+
<p><strong>Uso do Disco:</strong> {current_metrics.disk_usage:.1f}%</p>
|
| 1015 |
+
<p><small>Última atualização: {current_metrics.timestamp.strftime('%H:%M:%S')}</small></p>
|
| 1016 |
+
</div>
|
| 1017 |
+
"""
|
| 1018 |
+
|
| 1019 |
+
return html
|
| 1020 |
+
|
| 1021 |
+
except Exception as e:
|
| 1022 |
+
return f"<div style='color: red;'>Erro ao obter métricas: {str(e)}</div>"
|
| 1023 |
+
|
| 1024 |
+
def get_bot_statistics_display():
|
| 1025 |
+
"""Obtém display das estatísticas do bot."""
|
| 1026 |
+
global performance_monitor
|
| 1027 |
+
|
| 1028 |
+
if not performance_monitor:
|
| 1029 |
+
return "<div style='color: #666;'>Monitor de performance não disponível no modo standalone</div>"
|
| 1030 |
+
|
| 1031 |
+
try:
|
| 1032 |
+
bot_stats = performance_monitor.get_bot_stats()
|
| 1033 |
+
|
| 1034 |
+
success_rate = 0
|
| 1035 |
+
if bot_stats.total_analyses > 0:
|
| 1036 |
+
success_rate = (bot_stats.successful_analyses / bot_stats.total_analyses) * 100
|
| 1037 |
+
|
| 1038 |
+
html = f"""
|
| 1039 |
+
<div style='padding: 15px; border: 1px solid #ddd; border-radius: 8px;'>
|
| 1040 |
+
<h4>🤖 Estatísticas do Bot</h4>
|
| 1041 |
+
<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 15px;'>
|
| 1042 |
+
<div>
|
| 1043 |
+
<p><strong>Total de Análises:</strong> {bot_stats.total_analyses}</p>
|
| 1044 |
+
<p><strong>Sucessos:</strong> {bot_stats.successful_analyses}</p>
|
| 1045 |
+
<p><strong>Falhas:</strong> {bot_stats.failed_analyses}</p>
|
| 1046 |
+
<p><strong>Taxa de Sucesso:</strong> {success_rate:.1f}%</p>
|
| 1047 |
+
</div>
|
| 1048 |
+
<div>
|
| 1049 |
+
<p><strong>Tempo Médio:</strong> {bot_stats.average_analysis_time:.2f}s</p>
|
| 1050 |
+
<p><strong>Alertas Fibonacci:</strong> {bot_stats.fibonacci_alerts_count}</p>
|
| 1051 |
+
<p><strong>Última Atualização:</strong><br>
|
| 1052 |
+
<small>{bot_stats.last_update.strftime('%H:%M:%S') if bot_stats.last_update else 'N/A'}</small></p>
|
| 1053 |
+
</div>
|
| 1054 |
+
</div>
|
| 1055 |
+
|
| 1056 |
+
<h5>📊 Sinais Gerados:</h5>
|
| 1057 |
+
<div style='display: flex; gap: 10px;'>
|
| 1058 |
+
"""
|
| 1059 |
+
|
| 1060 |
+
for signal, count in bot_stats.signals_generated.items():
|
| 1061 |
+
color = {'COMPRAR': 'green', 'VENDER': 'red', 'AGUARDAR': 'orange'}.get(signal, '#666')
|
| 1062 |
+
html += f"<span style='background: {color}; color: white; padding: 5px 10px; border-radius: 15px; font-size: 12px;'>{signal}: {count}</span>"
|
| 1063 |
+
|
| 1064 |
+
html += "</div></div>"
|
| 1065 |
+
|
| 1066 |
+
return html
|
| 1067 |
+
|
| 1068 |
+
except Exception as e:
|
| 1069 |
+
return f"<div style='color: red;'>Erro ao obter estatísticas: {str(e)}</div>"
|
| 1070 |
+
|
| 1071 |
+
def get_performance_alerts_display():
|
| 1072 |
+
"""Obtém display dos alertas de performance."""
|
| 1073 |
+
global performance_monitor
|
| 1074 |
+
|
| 1075 |
+
if not performance_monitor:
|
| 1076 |
+
return "<div style='color: #666;'>Monitor de performance não disponível no modo standalone</div>"
|
| 1077 |
+
|
| 1078 |
+
try:
|
| 1079 |
+
summary = performance_monitor.get_performance_summary()
|
| 1080 |
+
active_alerts = summary.get('active_alerts', [])
|
| 1081 |
+
|
| 1082 |
+
if not active_alerts:
|
| 1083 |
+
return "<div style='color: green;'>✅ Nenhum alerta ativo - Sistema operando normalmente</div>"
|
| 1084 |
+
|
| 1085 |
+
html = "<div style='padding: 10px;'>"
|
| 1086 |
+
for alert in active_alerts:
|
| 1087 |
+
alert_text = {
|
| 1088 |
+
'high_cpu': '🔥 CPU Alto',
|
| 1089 |
+
'high_memory': '💾 Memória Alta',
|
| 1090 |
+
'slow_analysis': '🐌 Análise Lenta',
|
| 1091 |
+
'high_error_rate': '❌ Taxa de Erro Alta'
|
| 1092 |
+
}.get(alert, alert)
|
| 1093 |
+
|
| 1094 |
+
html += f"<div style='background: #ffebee; border-left: 4px solid #f44336; padding: 10px; margin: 5px 0;'>⚠️ {alert_text}</div>"
|
| 1095 |
+
|
| 1096 |
+
html += "</div>"
|
| 1097 |
+
return html
|
| 1098 |
+
|
| 1099 |
+
except Exception as e:
|
| 1100 |
+
return f"<div style='color: red;'>Erro ao obter alertas: {str(e)}</div>"
|
| 1101 |
+
|
| 1102 |
+
def get_optimization_suggestions_display():
|
| 1103 |
+
"""Obtém display das sugestões de otimização."""
|
| 1104 |
+
global performance_monitor
|
| 1105 |
+
|
| 1106 |
+
if not performance_monitor:
|
| 1107 |
+
return "<div style='color: #666;'>Monitor de performance não disponível no modo standalone</div>"
|
| 1108 |
+
|
| 1109 |
+
try:
|
| 1110 |
+
suggestions = performance_monitor.optimize_performance()
|
| 1111 |
+
|
| 1112 |
+
html = "<div style='padding: 10px;'>"
|
| 1113 |
+
for i, suggestion in enumerate(suggestions, 1):
|
| 1114 |
+
icon = "💡" if "normal" in suggestion.lower() else "⚠️"
|
| 1115 |
+
color = "#e8f5e8" if "normal" in suggestion.lower() else "#fff3cd"
|
| 1116 |
+
|
| 1117 |
+
html += f"<div style='background: {color}; padding: 10px; margin: 5px 0; border-radius: 5px;'>{icon} {suggestion}</div>"
|
| 1118 |
+
|
| 1119 |
+
html += "</div>"
|
| 1120 |
+
return html
|
| 1121 |
+
|
| 1122 |
+
except Exception as e:
|
| 1123 |
+
return f"<div style='color: red;'>Erro ao obter sugestões: {str(e)}</div>"
|
| 1124 |
+
|
| 1125 |
# Inicializar engines e criar interface
|
| 1126 |
initialize_engines()
|
| 1127 |
demo = create_interface()
|
config/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Pacote de configurações do Vampire Trading Bot."""
|
| 2 |
+
|
| 3 |
+
from .config import (
|
| 4 |
+
AppConfig,
|
| 5 |
+
TechnicalAnalysisConfig,
|
| 6 |
+
ScoringConfig,
|
| 7 |
+
TradingConfig,
|
| 8 |
+
UIConfig,
|
| 9 |
+
RegexPatterns,
|
| 10 |
+
AIConfig,
|
| 11 |
+
ExampleData,
|
| 12 |
+
FINANCIAL_MODELS
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
__all__ = [
|
| 16 |
+
'AppConfig',
|
| 17 |
+
'TechnicalAnalysisConfig',
|
| 18 |
+
'ScoringConfig',
|
| 19 |
+
'TradingConfig',
|
| 20 |
+
'UIConfig',
|
| 21 |
+
'RegexPatterns',
|
| 22 |
+
'AIConfig',
|
| 23 |
+
'ExampleData',
|
| 24 |
+
'FINANCIAL_MODELS'
|
| 25 |
+
]
|
config/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (480 Bytes). View file
|
|
|
config/__pycache__/config.cpython-313.pyc
ADDED
|
Binary file (6.21 kB). View file
|
|
|
config/config.py
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 📈 Vampire Trading Bot - Documentação
|
| 2 |
+
|
| 3 |
+
## Visão Geral
|
| 4 |
+
|
| 5 |
+
O Vampire Trading Bot é um sistema avançado de análise de mercado financeiro que combina análise técnica tradicional com inteligência artificial para fornecer sinais de trading precisos e análises de Fibonacci sofisticadas.
|
| 6 |
+
|
| 7 |
+
## 🏗️ Arquitetura do Sistema
|
| 8 |
+
|
| 9 |
+
O sistema é construído com uma arquitetura modular que permite flexibilidade e escalabilidade:
|
| 10 |
+
|
| 11 |
+
### Componentes Principais
|
| 12 |
+
|
| 13 |
+
- **Core Engine**: Motor principal de análise técnica e de sentimento
|
| 14 |
+
- **Fibonacci Engine**: Sistema avançado de análise de Fibonacci com padrões harmônicos
|
| 15 |
+
- **Real-time Integration**: Integração em tempo real com logs de trading
|
| 16 |
+
- **Performance Monitor**: Monitoramento de performance e métricas do sistema
|
| 17 |
+
- **UI Interface**: Interface web moderna construída com Gradio
|
| 18 |
+
|
| 19 |
+
### Fluxo de Dados
|
| 20 |
+
|
| 21 |
+
```
|
| 22 |
+
Dados de Mercado → Análise Técnica → Análise de Sentimento → Fibonacci → Interface
|
| 23 |
+
↓
|
| 24 |
+
Performance Monitor ← Real-time Integration ←────┘
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
## 📚 Documentação
|
| 28 |
+
|
| 29 |
+
- [Guia de Instalação](installation.md)
|
| 30 |
+
- [Arquitetura do Sistema](architecture.md)
|
| 31 |
+
- [APIs e Interfaces](api-reference.md)
|
| 32 |
+
- [Guia do Desenvolvedor](developer-guide.md)
|
| 33 |
+
- [Configuração](configuration.md)
|
| 34 |
+
- [Troubleshooting](troubleshooting.md)
|
| 35 |
+
|
| 36 |
+
## 🚀 Início Rápido
|
| 37 |
+
|
| 38 |
+
1. Clone o repositório
|
| 39 |
+
2. Instale as dependências: `pip install -r requirements.txt`
|
| 40 |
+
3. Execute o aplicativo: `python app.py`
|
| 41 |
+
4. Acesse a interface em `http://localhost:7860`
|
| 42 |
+
|
| 43 |
+
## 🔧 Tecnologias Utilizadas
|
| 44 |
+
|
| 45 |
+
- **Python 3.13+**
|
| 46 |
+
- **Gradio**: Interface web interativa
|
| 47 |
+
- **Transformers**: Modelos de IA para análise de sentimento
|
| 48 |
+
- **PyTorch**: Framework de deep learning
|
| 49 |
+
- **NumPy/Pandas**: Processamento de dados
|
| 50 |
+
- **Psutil**: Monitoramento de sistema
|
| 51 |
+
|
| 52 |
+
## 📊 Funcionalidades
|
| 53 |
+
|
| 54 |
+
### Análise Técnica
|
| 55 |
+
- RSI (Relative Strength Index)
|
| 56 |
+
- Médias Móveis Exponenciais (EMA)
|
| 57 |
+
- Bandas de Bollinger
|
| 58 |
+
- Análise de Volume e Momentum
|
| 59 |
+
- Detecção de Setups de Scalping
|
| 60 |
+
|
| 61 |
+
### Análise de Fibonacci
|
| 62 |
+
- Níveis de Retração
|
| 63 |
+
- Níveis de Extensão
|
| 64 |
+
- Projeções Temporais
|
| 65 |
+
- Zonas de Confluência
|
| 66 |
+
- Padrões Harmônicos
|
| 67 |
+
|
| 68 |
+
### Análise de Sentimento
|
| 69 |
+
- Processamento de texto com IA
|
| 70 |
+
- Modelos financeiros especializados
|
| 71 |
+
- Análise de palavras-chave
|
| 72 |
+
- Pontuação de sentimento
|
| 73 |
+
|
| 74 |
+
### Monitoramento
|
| 75 |
+
- Métricas de performance em tempo real
|
| 76 |
+
- Histórico de análises
|
| 77 |
+
- Alertas de sistema
|
| 78 |
+
- Estatísticas de precisão
|
| 79 |
+
|
| 80 |
+
## 📝 Licença
|
| 81 |
+
|
| 82 |
+
Este projeto é proprietário e confidencial.
|
| 83 |
+
|
| 84 |
+
## 🤝 Contribuição
|
| 85 |
+
|
| 86 |
+
Veja o [Guia do Desenvolvedor](developer-guide.md) para informações sobre como contribuir.
|
docs/api-reference.md
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 📚 Referência de APIs e Interfaces
|
| 2 |
+
|
| 3 |
+
## Visão Geral
|
| 4 |
+
|
| 5 |
+
Este documento descreve todas as APIs, interfaces e contratos entre os módulos do Vampire Trading Bot. Cada seção inclui exemplos de uso, parâmetros esperados e formatos de resposta.
|
| 6 |
+
|
| 7 |
+
## 🔧 Core Engines
|
| 8 |
+
|
| 9 |
+
### TechnicalAnalysisEngine
|
| 10 |
+
|
| 11 |
+
#### Classe Principal
|
| 12 |
+
```python
|
| 13 |
+
class TechnicalAnalysisEngine:
|
| 14 |
+
def __init__(self):
|
| 15 |
+
"""Inicializa o engine de análise técnica."""
|
| 16 |
+
|
| 17 |
+
def analyze_market_data(self, market_text: str) -> Dict[str, Any]:
|
| 18 |
+
"""Analisa dados de mercado a partir de texto.
|
| 19 |
+
|
| 20 |
+
Args:
|
| 21 |
+
market_text (str): Texto contendo dados de mercado
|
| 22 |
+
|
| 23 |
+
Returns:
|
| 24 |
+
Dict[str, Any]: Resultado da análise técnica
|
| 25 |
+
{
|
| 26 |
+
'market_data': MarketData,
|
| 27 |
+
'technical_signals': List[TechnicalSignal],
|
| 28 |
+
'scalping_setups': List[ScalpingSetup],
|
| 29 |
+
'risk_analysis': RiskAnalysis,
|
| 30 |
+
'overall_signal': str,
|
| 31 |
+
'confidence': float
|
| 32 |
+
}
|
| 33 |
+
"""
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
#### Estruturas de Dados
|
| 37 |
+
|
| 38 |
+
```python
|
| 39 |
+
@dataclass
|
| 40 |
+
class MarketData:
|
| 41 |
+
"""Dados básicos de mercado."""
|
| 42 |
+
symbol: str # Símbolo do ativo
|
| 43 |
+
current_price: float # Preço atual
|
| 44 |
+
variation: float # Variação absoluta
|
| 45 |
+
variation_percent: float # Variação percentual
|
| 46 |
+
high: float # Máxima do período
|
| 47 |
+
low: float # Mínima do período
|
| 48 |
+
volume: float # Volume negociado
|
| 49 |
+
timestamp: str # Timestamp da cotação
|
| 50 |
+
|
| 51 |
+
@dataclass
|
| 52 |
+
class TechnicalSignal:
|
| 53 |
+
"""Sinal de indicador técnico."""
|
| 54 |
+
indicator: str # Nome do indicador (RSI, EMA, etc.)
|
| 55 |
+
value: float # Valor atual do indicador
|
| 56 |
+
signal: str # Sinal gerado (COMPRAR, VENDER, NEUTRO)
|
| 57 |
+
confidence: float # Confiança do sinal (0-100)
|
| 58 |
+
description: str # Descrição detalhada
|
| 59 |
+
timestamp: str # Timestamp do cálculo
|
| 60 |
+
|
| 61 |
+
@dataclass
|
| 62 |
+
class ScalpingSetup:
|
| 63 |
+
"""Setup de scalping detectado."""
|
| 64 |
+
name: str # Nome do setup
|
| 65 |
+
entry_price: float # Preço de entrada
|
| 66 |
+
stop_loss: float # Stop loss
|
| 67 |
+
take_profit: float # Take profit
|
| 68 |
+
risk_reward: float # Relação risco/recompensa
|
| 69 |
+
confidence: float # Confiança do setup
|
| 70 |
+
timeframe: str # Timeframe recomendado
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
#### Exemplo de Uso
|
| 74 |
+
|
| 75 |
+
```python
|
| 76 |
+
engine = TechnicalAnalysisEngine()
|
| 77 |
+
|
| 78 |
+
market_text = """
|
| 79 |
+
📊 DADOS DE MERCADO - WINV25
|
| 80 |
+
Preço Atual: 140135.00000 ↗
|
| 81 |
+
Variação: +5.00000 (+0.00%)
|
| 82 |
+
Máxima: 140155.00000
|
| 83 |
+
Mínima: 140075.00000
|
| 84 |
+
Volume: 5023
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
result = engine.analyze_market_data(market_text)
|
| 88 |
+
print(f"Sinal geral: {result['overall_signal']}")
|
| 89 |
+
print(f"Confiança: {result['confidence']}%")
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
### SentimentAnalysisEngine
|
| 93 |
+
|
| 94 |
+
#### Classe Principal
|
| 95 |
+
```python
|
| 96 |
+
class SentimentAnalysisEngine:
|
| 97 |
+
def __init__(self, model_name: str = None):
|
| 98 |
+
"""Inicializa o engine de análise de sentimento.
|
| 99 |
+
|
| 100 |
+
Args:
|
| 101 |
+
model_name (str, optional): Nome do modelo a usar
|
| 102 |
+
"""
|
| 103 |
+
|
| 104 |
+
def analyze_sentiment(self, text: str) -> SentimentResult:
|
| 105 |
+
"""Analisa sentimento de um texto.
|
| 106 |
+
|
| 107 |
+
Args:
|
| 108 |
+
text (str): Texto para análise (max 512 tokens)
|
| 109 |
+
|
| 110 |
+
Returns:
|
| 111 |
+
SentimentResult: Resultado da análise
|
| 112 |
+
"""
|
| 113 |
+
|
| 114 |
+
def batch_analyze(self, texts: List[str]) -> List[SentimentResult]:
|
| 115 |
+
"""Analisa múltiplos textos em lote.
|
| 116 |
+
|
| 117 |
+
Args:
|
| 118 |
+
texts (List[str]): Lista de textos
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
List[SentimentResult]: Lista de resultados
|
| 122 |
+
"""
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
#### Estruturas de Dados
|
| 126 |
+
|
| 127 |
+
```python
|
| 128 |
+
@dataclass
|
| 129 |
+
class SentimentResult:
|
| 130 |
+
"""Resultado de análise de sentimento."""
|
| 131 |
+
text: str # Texto original
|
| 132 |
+
sentiment: str # POSITIVO, NEGATIVO, NEUTRO
|
| 133 |
+
confidence: float # Confiança (0-100)
|
| 134 |
+
scores: Dict[str, float] # Scores detalhados por classe
|
| 135 |
+
keywords: List[str] # Palavras-chave extraídas
|
| 136 |
+
financial_indicators: List[str] # Indicadores financeiros detectados
|
| 137 |
+
timestamp: str # Timestamp da análise
|
| 138 |
+
model_used: str # Modelo utilizado
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
#### Exemplo de Uso
|
| 142 |
+
|
| 143 |
+
```python
|
| 144 |
+
engine = SentimentAnalysisEngine()
|
| 145 |
+
|
| 146 |
+
text = "O mercado está muito otimista hoje, com forte alta nos índices."
|
| 147 |
+
result = engine.analyze_sentiment(text)
|
| 148 |
+
|
| 149 |
+
print(f"Sentimento: {result.sentiment}")
|
| 150 |
+
print(f"Confiança: {result.confidence}%")
|
| 151 |
+
print(f"Palavras-chave: {result.keywords}")
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
### AdvancedFibonacciEngine
|
| 155 |
+
|
| 156 |
+
#### Classe Principal
|
| 157 |
+
```python
|
| 158 |
+
class AdvancedFibonacciEngine:
|
| 159 |
+
def __init__(self):
|
| 160 |
+
"""Inicializa o engine de análise de Fibonacci."""
|
| 161 |
+
|
| 162 |
+
def perform_advanced_analysis(
|
| 163 |
+
self,
|
| 164 |
+
swing_high: float,
|
| 165 |
+
swing_low: float,
|
| 166 |
+
current_price: float,
|
| 167 |
+
historical_data: Optional[pd.DataFrame] = None
|
| 168 |
+
) -> AdvancedFibonacciAnalysis:
|
| 169 |
+
"""Realiza análise avançada de Fibonacci.
|
| 170 |
+
|
| 171 |
+
Args:
|
| 172 |
+
swing_high (float): Preço do swing high
|
| 173 |
+
swing_low (float): Preço do swing low
|
| 174 |
+
current_price (float): Preço atual
|
| 175 |
+
historical_data (pd.DataFrame, optional): Dados históricos
|
| 176 |
+
|
| 177 |
+
Returns:
|
| 178 |
+
AdvancedFibonacciAnalysis: Análise completa
|
| 179 |
+
"""
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
#### Estruturas de Dados
|
| 183 |
+
|
| 184 |
+
```python
|
| 185 |
+
@dataclass
|
| 186 |
+
class FibonacciLevel:
|
| 187 |
+
"""Nível de Fibonacci."""
|
| 188 |
+
level: float # Nível (0.236, 0.382, etc.)
|
| 189 |
+
price: float # Preço do nível
|
| 190 |
+
type: str # 'retracement', 'extension', 'projection'
|
| 191 |
+
ratio: float # Ratio de Fibonacci
|
| 192 |
+
distance_from_current: float # Distância do preço atual
|
| 193 |
+
strength: float # Força do nível (0-100)
|
| 194 |
+
|
| 195 |
+
@dataclass
|
| 196 |
+
class ConfluenceZone:
|
| 197 |
+
"""Zona de confluência."""
|
| 198 |
+
price_range: Tuple[float, float] # Faixa de preços
|
| 199 |
+
levels_count: int # Número de níveis na zona
|
| 200 |
+
strength: float # Força da zona
|
| 201 |
+
types: List[str] # Tipos de níveis presentes
|
| 202 |
+
|
| 203 |
+
@dataclass
|
| 204 |
+
class AdvancedFibonacciAnalysis:
|
| 205 |
+
"""Análise completa de Fibonacci."""
|
| 206 |
+
swing_high: float
|
| 207 |
+
swing_low: float
|
| 208 |
+
current_price: float
|
| 209 |
+
swing_range: float
|
| 210 |
+
retracement_levels: List[FibonacciLevel]
|
| 211 |
+
extension_levels: List[FibonacciLevel]
|
| 212 |
+
projection_levels: List[FibonacciLevel]
|
| 213 |
+
confluence_zones: List[ConfluenceZone]
|
| 214 |
+
harmonic_patterns: List[HarmonicPattern]
|
| 215 |
+
key_support: float
|
| 216 |
+
key_resistance: float
|
| 217 |
+
trend_direction: str
|
| 218 |
+
fibonacci_zone: str
|
| 219 |
+
overall_strength: float
|
| 220 |
+
trading_signal: str
|
| 221 |
+
alerts_count: int
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
## 🔄 Real-time Integration
|
| 225 |
+
|
| 226 |
+
### RealTimeIntegration
|
| 227 |
+
|
| 228 |
+
#### Classe Principal
|
| 229 |
+
```python
|
| 230 |
+
class RealTimeIntegration:
|
| 231 |
+
def __init__(self, log_file_path: str):
|
| 232 |
+
"""Inicializa integração em tempo real.
|
| 233 |
+
|
| 234 |
+
Args:
|
| 235 |
+
log_file_path (str): Caminho para arquivo de log
|
| 236 |
+
"""
|
| 237 |
+
|
| 238 |
+
def start(self):
|
| 239 |
+
"""Inicia monitoramento em tempo real."""
|
| 240 |
+
|
| 241 |
+
def stop(self):
|
| 242 |
+
"""Para monitoramento."""
|
| 243 |
+
|
| 244 |
+
def subscribe(self, callback: Callable[[BotEvent], None]):
|
| 245 |
+
"""Inscreve callback para eventos.
|
| 246 |
+
|
| 247 |
+
Args:
|
| 248 |
+
callback: Função a ser chamada para cada evento
|
| 249 |
+
"""
|
| 250 |
+
```
|
| 251 |
+
|
| 252 |
+
#### Estruturas de Dados
|
| 253 |
+
|
| 254 |
+
```python
|
| 255 |
+
@dataclass
|
| 256 |
+
class BotEvent:
|
| 257 |
+
"""Evento do bot em tempo real."""
|
| 258 |
+
timestamp: datetime
|
| 259 |
+
event_type: str # 'new_analysis', 'fibonacci_alert', etc.
|
| 260 |
+
data: Dict[str, Any] # Dados do evento
|
| 261 |
+
priority: str # 'low', 'normal', 'high', 'critical'
|
| 262 |
+
|
| 263 |
+
@dataclass
|
| 264 |
+
class RealTimeConfig:
|
| 265 |
+
"""Configuração de tempo real."""
|
| 266 |
+
log_file_path: str
|
| 267 |
+
check_interval: float # Intervalo de verificação (segundos)
|
| 268 |
+
max_queue_size: int # Tamanho máximo da fila
|
| 269 |
+
enable_notifications: bool # Habilitar notificações
|
| 270 |
+
auto_analysis: bool # Análise automática
|
| 271 |
+
backup_logs: bool # Backup de logs
|
| 272 |
+
```
|
| 273 |
+
|
| 274 |
+
## 📊 Performance Monitor
|
| 275 |
+
|
| 276 |
+
### PerformanceMonitor
|
| 277 |
+
|
| 278 |
+
#### Classe Principal
|
| 279 |
+
```python
|
| 280 |
+
class PerformanceMonitor:
|
| 281 |
+
def __init__(self, max_metrics_history: int = 1000):
|
| 282 |
+
"""Inicializa monitor de performance."""
|
| 283 |
+
|
| 284 |
+
def start_monitoring(self, interval: float = 5.0):
|
| 285 |
+
"""Inicia monitoramento.
|
| 286 |
+
|
| 287 |
+
Args:
|
| 288 |
+
interval (float): Intervalo entre coletas (segundos)
|
| 289 |
+
"""
|
| 290 |
+
|
| 291 |
+
def record_analysis_time(self, analysis_time: float):
|
| 292 |
+
"""Registra tempo de análise.
|
| 293 |
+
|
| 294 |
+
Args:
|
| 295 |
+
analysis_time (float): Tempo em segundos
|
| 296 |
+
"""
|
| 297 |
+
|
| 298 |
+
def get_performance_summary(self) -> Dict[str, Any]:
|
| 299 |
+
"""Retorna resumo de performance.
|
| 300 |
+
|
| 301 |
+
Returns:
|
| 302 |
+
Dict com métricas de performance
|
| 303 |
+
"""
|
| 304 |
+
```
|
| 305 |
+
|
| 306 |
+
#### Estruturas de Dados
|
| 307 |
+
|
| 308 |
+
```python
|
| 309 |
+
@dataclass
|
| 310 |
+
class PerformanceMetrics:
|
| 311 |
+
"""Métricas de performance."""
|
| 312 |
+
timestamp: datetime
|
| 313 |
+
cpu_usage: float # Uso de CPU (%)
|
| 314 |
+
memory_usage: float # Uso de memória (%)
|
| 315 |
+
memory_available: float # Memória disponível (MB)
|
| 316 |
+
disk_usage: float # Uso de disco (%)
|
| 317 |
+
analysis_time: float # Tempo de análise (s)
|
| 318 |
+
events_processed: int # Eventos processados
|
| 319 |
+
errors_count: int # Contagem de erros
|
| 320 |
+
bot_signals_count: int # Sinais do bot
|
| 321 |
+
fibonacci_alerts_count: int # Alertas de Fibonacci
|
| 322 |
+
```
|
| 323 |
+
|
| 324 |
+
## 🔍 Log Parser
|
| 325 |
+
|
| 326 |
+
### VampireBotLogParser
|
| 327 |
+
|
| 328 |
+
#### Classe Principal
|
| 329 |
+
```python
|
| 330 |
+
class VampireBotLogParser:
|
| 331 |
+
def __init__(self):
|
| 332 |
+
"""Inicializa parser de logs."""
|
| 333 |
+
|
| 334 |
+
def parse_log_content(self, log_content: str) -> Optional[BotAnalysis]:
|
| 335 |
+
"""Faz parse de conteúdo de log.
|
| 336 |
+
|
| 337 |
+
Args:
|
| 338 |
+
log_content (str): Conteúdo do log
|
| 339 |
+
|
| 340 |
+
Returns:
|
| 341 |
+
BotAnalysis ou None se parsing falhar
|
| 342 |
+
"""
|
| 343 |
+
|
| 344 |
+
def parse_log_file(self, file_path: str) -> Optional[BotAnalysis]:
|
| 345 |
+
"""Faz parse de arquivo de log.
|
| 346 |
+
|
| 347 |
+
Args:
|
| 348 |
+
file_path (str): Caminho do arquivo
|
| 349 |
+
|
| 350 |
+
Returns:
|
| 351 |
+
BotAnalysis ou None se parsing falhar
|
| 352 |
+
"""
|
| 353 |
+
```
|
| 354 |
+
|
| 355 |
+
#### Estruturas de Dados
|
| 356 |
+
|
| 357 |
+
```python
|
| 358 |
+
@dataclass
|
| 359 |
+
class BotAnalysis:
|
| 360 |
+
"""Análise completa do bot."""
|
| 361 |
+
analysis_number: int
|
| 362 |
+
timestamp: str
|
| 363 |
+
market_data: MarketData
|
| 364 |
+
technical_indicators: TechnicalIndicators
|
| 365 |
+
fibonacci_analysis: FibonacciAnalysis
|
| 366 |
+
performance_time: Optional[float] = None
|
| 367 |
+
|
| 368 |
+
@dataclass
|
| 369 |
+
class TechnicalIndicators:
|
| 370 |
+
"""Indicadores técnicos do log."""
|
| 371 |
+
rsi: float
|
| 372 |
+
rsi_status: str
|
| 373 |
+
ema_fast: float
|
| 374 |
+
ema_slow: float
|
| 375 |
+
ema_trend: str
|
| 376 |
+
bollinger_status: str
|
| 377 |
+
bollinger_upper: float
|
| 378 |
+
bollinger_lower: float
|
| 379 |
+
atr: float
|
| 380 |
+
volatility: str
|
| 381 |
+
volatility_multiplier: float
|
| 382 |
+
```
|
| 383 |
+
|
| 384 |
+
## 🎨 UI Interface
|
| 385 |
+
|
| 386 |
+
### GradioInterface
|
| 387 |
+
|
| 388 |
+
#### Classe Principal
|
| 389 |
+
```python
|
| 390 |
+
class GradioInterface:
|
| 391 |
+
def __init__(self):
|
| 392 |
+
"""Inicializa interface Gradio."""
|
| 393 |
+
|
| 394 |
+
def create_interface(self) -> gr.Blocks:
|
| 395 |
+
"""Cria interface completa.
|
| 396 |
+
|
| 397 |
+
Returns:
|
| 398 |
+
gr.Blocks: Interface Gradio configurada
|
| 399 |
+
"""
|
| 400 |
+
|
| 401 |
+
def launch(self, **kwargs):
|
| 402 |
+
"""Lança a interface.
|
| 403 |
+
|
| 404 |
+
Args:
|
| 405 |
+
**kwargs: Argumentos para gr.launch()
|
| 406 |
+
"""
|
| 407 |
+
```
|
| 408 |
+
|
| 409 |
+
#### Componentes da Interface
|
| 410 |
+
|
| 411 |
+
```python
|
| 412 |
+
class UIComponents:
|
| 413 |
+
"""Componentes reutilizáveis da UI."""
|
| 414 |
+
|
| 415 |
+
@staticmethod
|
| 416 |
+
def create_market_input() -> gr.Textbox:
|
| 417 |
+
"""Cria campo de entrada de dados de mercado."""
|
| 418 |
+
|
| 419 |
+
@staticmethod
|
| 420 |
+
def create_sentiment_input() -> gr.Textbox:
|
| 421 |
+
"""Cria campo de entrada para análise de sentimento."""
|
| 422 |
+
|
| 423 |
+
@staticmethod
|
| 424 |
+
def create_fibonacci_inputs() -> Tuple[gr.Number, gr.Number, gr.Number]:
|
| 425 |
+
"""Cria campos para análise de Fibonacci."""
|
| 426 |
+
```
|
| 427 |
+
|
| 428 |
+
## 🛠️ Utilities
|
| 429 |
+
|
| 430 |
+
### Utilitários de Validação
|
| 431 |
+
|
| 432 |
+
```python
|
| 433 |
+
class ValidationUtils:
|
| 434 |
+
@staticmethod
|
| 435 |
+
def validate_market_data(data: Dict[str, Any]) -> bool:
|
| 436 |
+
"""Valida dados de mercado.
|
| 437 |
+
|
| 438 |
+
Args:
|
| 439 |
+
data: Dicionário com dados de mercado
|
| 440 |
+
|
| 441 |
+
Returns:
|
| 442 |
+
bool: True se válido
|
| 443 |
+
"""
|
| 444 |
+
|
| 445 |
+
@staticmethod
|
| 446 |
+
def validate_text_input(text: str) -> bool:
|
| 447 |
+
"""Valida entrada de texto.
|
| 448 |
+
|
| 449 |
+
Args:
|
| 450 |
+
text: Texto a validar
|
| 451 |
+
|
| 452 |
+
Returns:
|
| 453 |
+
bool: True se válido
|
| 454 |
+
"""
|
| 455 |
+
```
|
| 456 |
+
|
| 457 |
+
### Utilitários de Formatação
|
| 458 |
+
|
| 459 |
+
```python
|
| 460 |
+
class FormatUtils:
|
| 461 |
+
@staticmethod
|
| 462 |
+
def format_price(price: float) -> str:
|
| 463 |
+
"""Formata preço com separadores."""
|
| 464 |
+
|
| 465 |
+
@staticmethod
|
| 466 |
+
def format_percentage(value: float) -> str:
|
| 467 |
+
"""Formata porcentagem com sinal."""
|
| 468 |
+
|
| 469 |
+
@staticmethod
|
| 470 |
+
def format_analysis_result(result: Dict[str, Any]) -> str:
|
| 471 |
+
"""Formata resultado de análise para exibição."""
|
| 472 |
+
```
|
| 473 |
+
|
| 474 |
+
## 📋 Códigos de Erro
|
| 475 |
+
|
| 476 |
+
### Códigos de Sistema
|
| 477 |
+
- `SYS_001`: Erro de inicialização
|
| 478 |
+
- `SYS_002`: Erro de configuração
|
| 479 |
+
- `SYS_003`: Erro de memória insuficiente
|
| 480 |
+
|
| 481 |
+
### Códigos de Análise
|
| 482 |
+
- `ANA_001`: Dados de mercado inválidos
|
| 483 |
+
- `ANA_002`: Erro no modelo de IA
|
| 484 |
+
- `ANA_003`: Timeout de análise
|
| 485 |
+
- `ANA_004`: Erro de parsing de texto
|
| 486 |
+
|
| 487 |
+
### Códigos de Interface
|
| 488 |
+
- `UI_001`: Erro de renderização
|
| 489 |
+
- `UI_002`: Entrada inválida
|
| 490 |
+
- `UI_003`: Erro de comunicação
|
| 491 |
+
|
| 492 |
+
## 🔧 Configuração de APIs
|
| 493 |
+
|
| 494 |
+
### Variáveis de Ambiente
|
| 495 |
+
|
| 496 |
+
```bash
|
| 497 |
+
# Configurações de modelo
|
| 498 |
+
FINBERT_MODEL_PATH=/path/to/model
|
| 499 |
+
MAX_TEXT_LENGTH=512
|
| 500 |
+
|
| 501 |
+
# Configurações de performance
|
| 502 |
+
MAX_WORKERS=4
|
| 503 |
+
CACHE_SIZE=1000
|
| 504 |
+
|
| 505 |
+
# Configurações de logging
|
| 506 |
+
LOG_LEVEL=INFO
|
| 507 |
+
LOG_FILE_PATH=/path/to/logs
|
| 508 |
+
```
|
| 509 |
+
|
| 510 |
+
### Configuração via Arquivo
|
| 511 |
+
|
| 512 |
+
```python
|
| 513 |
+
# config.py
|
| 514 |
+
class APIConfig:
|
| 515 |
+
MAX_TEXT_LENGTH = 512
|
| 516 |
+
TIMEOUT_SECONDS = 30
|
| 517 |
+
RETRY_ATTEMPTS = 3
|
| 518 |
+
CACHE_TTL = 3600
|
| 519 |
+
```
|
| 520 |
+
|
| 521 |
+
## 📝 Exemplos de Integração
|
| 522 |
+
|
| 523 |
+
### Exemplo Completo
|
| 524 |
+
|
| 525 |
+
```python
|
| 526 |
+
from market_analysis import TechnicalAnalysisEngine
|
| 527 |
+
from sentiment_analysis import SentimentAnalysisEngine
|
| 528 |
+
from fibonacci_analysis import AdvancedFibonacciEngine
|
| 529 |
+
|
| 530 |
+
# Inicializar engines
|
| 531 |
+
tech_engine = TechnicalAnalysisEngine()
|
| 532 |
+
sentiment_engine = SentimentAnalysisEngine()
|
| 533 |
+
fib_engine = AdvancedFibonacciEngine()
|
| 534 |
+
|
| 535 |
+
# Dados de entrada
|
| 536 |
+
market_text = "Preço: 140135, Variação: +5"
|
| 537 |
+
sentiment_text = "Mercado muito otimista hoje"
|
| 538 |
+
|
| 539 |
+
# Análises
|
| 540 |
+
tech_result = tech_engine.analyze_market_data(market_text)
|
| 541 |
+
sentiment_result = sentiment_engine.analyze_sentiment(sentiment_text)
|
| 542 |
+
fib_result = fib_engine.perform_advanced_analysis(140570, 139540, 140135)
|
| 543 |
+
|
| 544 |
+
# Consolidar resultados
|
| 545 |
+
final_result = {
|
| 546 |
+
'technical': tech_result,
|
| 547 |
+
'sentiment': sentiment_result,
|
| 548 |
+
'fibonacci': fib_result,
|
| 549 |
+
'timestamp': datetime.now().isoformat()
|
| 550 |
+
}
|
| 551 |
+
|
| 552 |
+
print(json.dumps(final_result, indent=2, default=str))
|
| 553 |
+
```
|
| 554 |
+
|
| 555 |
+
## 🚀 Próximas Versões da API
|
| 556 |
+
|
| 557 |
+
### v2.0 (Planejado)
|
| 558 |
+
- REST API completa
|
| 559 |
+
- WebSocket para tempo real
|
| 560 |
+
- Autenticação e autorização
|
| 561 |
+
- Rate limiting
|
| 562 |
+
- Documentação OpenAPI/Swagger
|
| 563 |
+
|
| 564 |
+
### v3.0 (Futuro)
|
| 565 |
+
- GraphQL API
|
| 566 |
+
- Microserviços
|
| 567 |
+
- API Gateway
|
| 568 |
+
- Métricas avançadas
|
| 569 |
+
- Multi-tenancy
|
docs/architecture.md
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🏗️ Arquitetura do Sistema
|
| 2 |
+
|
| 3 |
+
## Visão Geral
|
| 4 |
+
|
| 5 |
+
O Vampire Trading Bot é 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 |
+
│ VAMPIRE TRADING BOT │
|
| 12 |
+
├─────────────────────────────────────────────────────────────────┤
|
| 13 |
+
│ 📱 PRESENTATION LAYER │
|
| 14 |
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 15 |
+
│ │ Gradio UI │ │ REST API │ │ WebSocket │ │
|
| 16 |
+
│ │ (ui.py) │ │ (future) │ │ (future) │ │
|
| 17 |
+
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 18 |
+
├─────────────────────────────────────────────────────────────────┤
|
| 19 |
+
│ 🧠 BUSINESS LOGIC LAYER │
|
| 20 |
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 21 |
+
│ │ Technical │ │ Sentiment │ │ Fibonacci │ │
|
| 22 |
+
│ │ Analysis Engine │ │ Analysis Engine │ │ Analysis Engine │ │
|
| 23 |
+
│ │ (market_*.py) │ │ (sentiment_*.py)│ │ (fibonacci_*.py)│ │
|
| 24 |
+
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 25 |
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 26 |
+
│ │ Real-time │ │ Performance │ │ Log Parser │ │
|
| 27 |
+
│ │ Integration │ │ Monitor │ │ (log_parser.py) │ │
|
| 28 |
+
│ │ (real_time_*.py)│ │ (performance_*) │ │ │ │
|
| 29 |
+
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 30 |
+
├─────────────────────────────────────────────────────────────────┤
|
| 31 |
+
│ 🔧 INFRASTRUCTURE LAYER │
|
| 32 |
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 33 |
+
│ │ Configuration │ │ Utilities │ │ Logging │ │
|
| 34 |
+
│ │ (config.py) │ │ (utils.py) │ │ (built-in) │ │
|
| 35 |
+
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 36 |
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
| 37 |
+
│ │ AI Models │ │ File System │ │ Threading │ │
|
| 38 |
+
│ │ (transformers) │ │ (pathlib) │ │ (asyncio) │ │
|
| 39 |
+
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
| 40 |
+
└─────────────────────────────────────────────────────────────────┘
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
## Componentes Principais
|
| 44 |
+
|
| 45 |
+
### 1. 📱 Camada de Apresentação
|
| 46 |
+
|
| 47 |
+
#### Gradio Interface (`ui.py`)
|
| 48 |
+
- **Responsabilidade**: Interface web interativa
|
| 49 |
+
- **Tecnologia**: Gradio 4.44.0
|
| 50 |
+
- **Funcionalidades**:
|
| 51 |
+
- Dashboard principal com métricas em tempo real
|
| 52 |
+
- Formulários de entrada para análise de texto
|
| 53 |
+
- Visualização de resultados de análise
|
| 54 |
+
- Gráficos e indicadores visuais
|
| 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 |
+
#### Technical Analysis Engine (`market_analysis.py`)
|
| 71 |
+
- **Responsabilidade**: Análise técnica de mercado
|
| 72 |
+
- **Componentes**:
|
| 73 |
+
- `MarketDataParser`: Parsing de dados de mercado
|
| 74 |
+
- `RSIAnalyzer`: Análise de RSI
|
| 75 |
+
- `EMAAnalyzer`: Análise de médias móveis
|
| 76 |
+
- `BollingerAnalyzer`: Análise de Bandas de Bollinger
|
| 77 |
+
- `ScalpingSetupDetector`: Detecção de setups
|
| 78 |
+
- `RiskCalculator`: Cálculo de risco/recompensa
|
| 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 |
+
#### Sentiment Analysis Engine (`sentiment_analysis.py`)
|
| 90 |
+
- **Responsabilidade**: Análise de sentimento com IA
|
| 91 |
+
- **Componentes**:
|
| 92 |
+
- `ModelManager`: Gerenciamento de modelos de IA
|
| 93 |
+
- `TextPreprocessor`: Pré-processamento de texto
|
| 94 |
+
- `SentimentAnalyzer`: Análise de sentimento
|
| 95 |
+
- `SentimentScorer`: Pontuação de sentimento
|
| 96 |
+
|
| 97 |
+
```python
|
| 98 |
+
class SentimentAnalysisEngine:
|
| 99 |
+
def __init__(self):
|
| 100 |
+
self.model_manager = ModelManager()
|
| 101 |
+
self.preprocessor = TextPreprocessor()
|
| 102 |
+
self.analyzer = SentimentAnalyzer()
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
#### Advanced Fibonacci Engine (`fibonacci_analysis.py`)
|
| 106 |
+
- **Responsabilidade**: Análise avançada de Fibonacci
|
| 107 |
+
- **Funcionalidades**:
|
| 108 |
+
- Níveis de retração e extensão
|
| 109 |
+
- Zonas de confluência
|
| 110 |
+
- Padrões harmônicos
|
| 111 |
+
- Projeções temporais
|
| 112 |
+
- Análise de força dos níveis
|
| 113 |
+
|
| 114 |
+
```python
|
| 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 |
+
#### Real-time Integration (`real_time_integration.py`)
|
| 123 |
+
- **Responsabilidade**: Integração em tempo real
|
| 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 |
+
- Extração de dados de mercado
|
| 141 |
+
- Parsing de indicadores técnicos
|
| 142 |
+
- Análise de Fibonacci dos logs
|
| 143 |
+
|
| 144 |
+
### 3. 🔧 Camada de Infraestrutura
|
| 145 |
+
|
| 146 |
+
#### Configuration (`config.py`)
|
| 147 |
+
- **Responsabilidade**: Configurações centralizadas
|
| 148 |
+
- **Seções**:
|
| 149 |
+
- Modelos de IA
|
| 150 |
+
- Parâmetros de análise técnica
|
| 151 |
+
- Configurações de UI
|
| 152 |
+
- Configurações de trading
|
| 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. Fluxo de Análise Principal
|
| 165 |
+
|
| 166 |
+
```
|
| 167 |
+
1. Entrada de Dados
|
| 168 |
+
├── Texto do usuário (UI)
|
| 169 |
+
├── Dados de mercado (logs)
|
| 170 |
+
└── Parâmetros de configuração
|
| 171 |
+
|
| 172 |
+
2. Processamento
|
| 173 |
+
├── Análise Técnica
|
| 174 |
+
│ ├── Parsing de dados
|
| 175 |
+
│ ├── Cálculo de indicadores
|
| 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. Fluxo de Monitoramento
|
| 198 |
+
|
| 199 |
+
```
|
| 200 |
+
Sistema → Performance Monitor → Métricas → Dashboard
|
| 201 |
+
↓
|
| 202 |
+
Logs → Log Parser → Eventos → Real-time Integration → UI
|
| 203 |
+
```
|
| 204 |
+
|
| 205 |
+
## Padrões de Design Utilizados
|
| 206 |
+
|
| 207 |
+
### 1. **Strategy Pattern**
|
| 208 |
+
- Diferentes analisadores técnicos
|
| 209 |
+
- Múltiplos modelos de IA
|
| 210 |
+
- Estratégias de formatação
|
| 211 |
+
|
| 212 |
+
### 2. **Observer Pattern**
|
| 213 |
+
- Sistema de eventos em tempo real
|
| 214 |
+
- Notificações de performance
|
| 215 |
+
- Atualizações de UI
|
| 216 |
+
|
| 217 |
+
### 3. **Factory Pattern**
|
| 218 |
+
- Criação de analisadores
|
| 219 |
+
- Instanciação de modelos
|
| 220 |
+
- Geração de interfaces
|
| 221 |
+
|
| 222 |
+
### 4. **Singleton Pattern**
|
| 223 |
+
- Monitor de performance
|
| 224 |
+
- Configurações globais
|
| 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 |
+
1. Criar classe herdando de `BaseAnalyzer`
|
| 269 |
+
2. Implementar método `analyze()`
|
| 270 |
+
3. Registrar no `TechnicalAnalysisEngine`
|
| 271 |
+
|
| 272 |
+
### Adicionando Novos Modelos de IA
|
| 273 |
+
|
| 274 |
+
1. Adicionar modelo em `config.py`
|
| 275 |
+
2. Implementar carregamento no `ModelManager`
|
| 276 |
+
3. Testar compatibilidade
|
| 277 |
+
|
| 278 |
+
### Adicionando Novas Interfaces
|
| 279 |
+
|
| 280 |
+
1. Implementar interface seguindo padrão
|
| 281 |
+
2. Integrar com engines existentes
|
| 282 |
+
3. Configurar roteamento
|
| 283 |
+
|
| 284 |
+
## Performance e Otimização
|
| 285 |
+
|
| 286 |
+
### Estratégias de Cache
|
| 287 |
+
- Cache de modelos de IA
|
| 288 |
+
- Cache de cálculos técnicos
|
| 289 |
+
- Cache de resultados de análise
|
| 290 |
+
|
| 291 |
+
### Processamento Assíncrono
|
| 292 |
+
- Análises paralelas
|
| 293 |
+
- Carregamento assíncrono de modelos
|
| 294 |
+
- Processamento de eventos em background
|
| 295 |
+
|
| 296 |
+
### Otimização de Memória
|
| 297 |
+
- Lazy loading de modelos
|
| 298 |
+
- Limpeza automática de cache
|
| 299 |
+
- Monitoramento de uso de memória
|
| 300 |
+
|
| 301 |
+
## Segurança
|
| 302 |
+
|
| 303 |
+
### Validação de Entrada
|
| 304 |
+
- Sanitização de texto
|
| 305 |
+
- Validação de parâmetros
|
| 306 |
+
- Limites de tamanho
|
| 307 |
+
|
| 308 |
+
### Tratamento de Erros
|
| 309 |
+
- Try-catch abrangente
|
| 310 |
+
- Fallbacks para modo standalone
|
| 311 |
+
- Logging detalhado de erros
|
| 312 |
+
|
| 313 |
+
### Isolamento de Recursos
|
| 314 |
+
- Ambientes virtuais
|
| 315 |
+
- Limitação de recursos
|
| 316 |
+
- Timeouts de operação
|
| 317 |
+
|
| 318 |
+
## Monitoramento e Observabilidade
|
| 319 |
+
|
| 320 |
+
### Métricas Coletadas
|
| 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
|
docs/configuration.md
ADDED
|
@@ -0,0 +1,606 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ⚙️ Guia de Configuração
|
| 2 |
+
|
| 3 |
+
## Visão Geral
|
| 4 |
+
|
| 5 |
+
Este documento descreve todas as opções de configuração disponíveis no Vampire Trading Bot, incluindo configurações de modelos de IA, análise técnica, interface do usuário e performance.
|
| 6 |
+
|
| 7 |
+
## 📁 Arquivo Principal de Configuração
|
| 8 |
+
|
| 9 |
+
Todas as configurações estão centralizadas no arquivo `config.py`. Este arquivo contém classes de configuração organizadas por funcionalidade.
|
| 10 |
+
|
| 11 |
+
### Estrutura do Arquivo
|
| 12 |
+
|
| 13 |
+
```python
|
| 14 |
+
# config.py
|
| 15 |
+
class FinancialModels: # Modelos de IA
|
| 16 |
+
class TechnicalAnalysis: # Análise técnica
|
| 17 |
+
class ScoringConfig: # Pontuação e confiança
|
| 18 |
+
class TradingConfig: # Configurações de trading
|
| 19 |
+
class UIConfig: # Interface do usuário
|
| 20 |
+
class RegexPatterns: # Padrões de extração
|
| 21 |
+
class AIConfig: # Configurações de IA
|
| 22 |
+
class AppConfig: # Configurações gerais
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
## 🤖 Configuração de Modelos de IA
|
| 26 |
+
|
| 27 |
+
### FinancialModels
|
| 28 |
+
|
| 29 |
+
```python
|
| 30 |
+
class FinancialModels:
|
| 31 |
+
"""Modelos de IA para análise financeira."""
|
| 32 |
+
|
| 33 |
+
# Lista de modelos disponíveis (ordenados por performance/tamanho)
|
| 34 |
+
FINANCIAL_MODELS = [
|
| 35 |
+
"nlptown/bert-base-multilingual-uncased-sentiment", # Leve, multilíngue
|
| 36 |
+
"cardiffnlp/twitter-roberta-base-sentiment-latest", # Médio, redes sociais
|
| 37 |
+
"ProsusAI/finbert", # Pesado, financeiro
|
| 38 |
+
"mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis"
|
| 39 |
+
]
|
| 40 |
+
|
| 41 |
+
# Modelo padrão (usado se não especificado)
|
| 42 |
+
DEFAULT_MODEL = FINANCIAL_MODELS[0]
|
| 43 |
+
|
| 44 |
+
# Modelos alternativos para fallback
|
| 45 |
+
FALLBACK_MODELS = [
|
| 46 |
+
"cardiffnlp/twitter-roberta-base-sentiment-latest",
|
| 47 |
+
"nlptown/bert-base-multilingual-uncased-sentiment"
|
| 48 |
+
]
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
#### Como Configurar
|
| 52 |
+
|
| 53 |
+
1. **Escolher Modelo por Performance**:
|
| 54 |
+
```python
|
| 55 |
+
# Para melhor performance (mais leve)
|
| 56 |
+
DEFAULT_MODEL = "nlptown/bert-base-multilingual-uncased-sentiment"
|
| 57 |
+
|
| 58 |
+
# Para melhor precisão (mais pesado)
|
| 59 |
+
DEFAULT_MODEL = "ProsusAI/finbert"
|
| 60 |
+
```
|
| 61 |
+
|
| 62 |
+
2. **Adicionar Novos Modelos**:
|
| 63 |
+
```python
|
| 64 |
+
FINANCIAL_MODELS.append("seu-modelo/nome-do-modelo")
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
### AIConfig
|
| 68 |
+
|
| 69 |
+
```python
|
| 70 |
+
class AIConfig:
|
| 71 |
+
"""Configurações gerais de IA."""
|
| 72 |
+
|
| 73 |
+
# Tamanho máximo de texto para análise
|
| 74 |
+
MAX_TEXT_LENGTH = 512
|
| 75 |
+
|
| 76 |
+
# Pipeline de análise de sentimento
|
| 77 |
+
SENTIMENT_PIPELINE = "sentiment-analysis"
|
| 78 |
+
|
| 79 |
+
# Configurações de cache
|
| 80 |
+
ENABLE_MODEL_CACHE = True
|
| 81 |
+
CACHE_SIZE = 100
|
| 82 |
+
|
| 83 |
+
# Timeout para carregamento de modelos (segundos)
|
| 84 |
+
MODEL_LOAD_TIMEOUT = 60
|
| 85 |
+
|
| 86 |
+
# Usar GPU se disponível
|
| 87 |
+
USE_GPU = True
|
| 88 |
+
|
| 89 |
+
# Batch size para processamento
|
| 90 |
+
BATCH_SIZE = 8
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
#### Configurações Avançadas de IA
|
| 94 |
+
|
| 95 |
+
```python
|
| 96 |
+
# Configuração de memória
|
| 97 |
+
AIConfig.MAX_TEXT_LENGTH = 256 # Reduzir para economizar memória
|
| 98 |
+
AIConfig.BATCH_SIZE = 4 # Reduzir batch size
|
| 99 |
+
|
| 100 |
+
# Configuração de performance
|
| 101 |
+
AIConfig.USE_GPU = False # Forçar uso de CPU
|
| 102 |
+
AIConfig.ENABLE_MODEL_CACHE = False # Desabilitar cache
|
| 103 |
+
|
| 104 |
+
# Configuração de timeout
|
| 105 |
+
AIConfig.MODEL_LOAD_TIMEOUT = 120 # Aumentar timeout
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
## 📊 Configuração de Análise Técnica
|
| 109 |
+
|
| 110 |
+
### TechnicalAnalysis
|
| 111 |
+
|
| 112 |
+
```python
|
| 113 |
+
class TechnicalAnalysis:
|
| 114 |
+
"""Configurações para análise técnica."""
|
| 115 |
+
|
| 116 |
+
# Configurações do RSI
|
| 117 |
+
RSI_PERIOD = 14
|
| 118 |
+
RSI_OVERSOLD = 30
|
| 119 |
+
RSI_OVERBOUGHT = 70
|
| 120 |
+
|
| 121 |
+
# Configurações das Bandas de Bollinger
|
| 122 |
+
BOLLINGER_PERIOD = 20
|
| 123 |
+
BOLLINGER_STD = 2
|
| 124 |
+
|
| 125 |
+
# Configurações das EMAs
|
| 126 |
+
EMA_FAST_PERIOD = 9
|
| 127 |
+
EMA_SLOW_PERIOD = 21
|
| 128 |
+
|
| 129 |
+
# Configurações de Volume
|
| 130 |
+
VOLUME_MA_PERIOD = 20
|
| 131 |
+
HIGH_VOLUME_MULTIPLIER = 1.5
|
| 132 |
+
|
| 133 |
+
# Configurações de Momentum
|
| 134 |
+
MOMENTUM_PERIOD = 10
|
| 135 |
+
MOMENTUM_THRESHOLD = 0.02
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
#### Personalização de Indicadores
|
| 139 |
+
|
| 140 |
+
```python
|
| 141 |
+
# RSI mais sensível
|
| 142 |
+
TechnicalAnalysis.RSI_PERIOD = 10
|
| 143 |
+
TechnicalAnalysis.RSI_OVERSOLD = 25
|
| 144 |
+
TechnicalAnalysis.RSI_OVERBOUGHT = 75
|
| 145 |
+
|
| 146 |
+
# Bandas de Bollinger mais apertadas
|
| 147 |
+
TechnicalAnalysis.BOLLINGER_STD = 1.5
|
| 148 |
+
|
| 149 |
+
# EMAs mais rápidas
|
| 150 |
+
TechnicalAnalysis.EMA_FAST_PERIOD = 5
|
| 151 |
+
TechnicalAnalysis.EMA_SLOW_PERIOD = 13
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
## 🎯 Configuração de Trading
|
| 155 |
+
|
| 156 |
+
### TradingConfig
|
| 157 |
+
|
| 158 |
+
```python
|
| 159 |
+
class TradingConfig:
|
| 160 |
+
"""Configurações de trading e gestão de risco."""
|
| 161 |
+
|
| 162 |
+
# Stop Loss padrão (percentual)
|
| 163 |
+
DEFAULT_STOP_LOSS = 0.5 # 0.5%
|
| 164 |
+
|
| 165 |
+
# Take Profit padrão (percentual)
|
| 166 |
+
DEFAULT_TAKE_PROFIT = 1.0 # 1.0%
|
| 167 |
+
|
| 168 |
+
# Relação risco/recompensa mínima
|
| 169 |
+
MIN_RISK_REWARD = 1.5
|
| 170 |
+
|
| 171 |
+
# Tamanho de posição padrão
|
| 172 |
+
DEFAULT_POSITION_SIZE = 1.0
|
| 173 |
+
|
| 174 |
+
# Configurações de scalping
|
| 175 |
+
SCALPING_TIMEFRAMES = ["1m", "5m", "15m"]
|
| 176 |
+
SCALPING_MIN_VOLUME = 1000
|
| 177 |
+
|
| 178 |
+
# Configurações de volatilidade
|
| 179 |
+
LOW_VOLATILITY_THRESHOLD = 0.5
|
| 180 |
+
HIGH_VOLATILITY_THRESHOLD = 2.0
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
#### Estratégias de Risco
|
| 184 |
+
|
| 185 |
+
```python
|
| 186 |
+
# Estratégia conservadora
|
| 187 |
+
TradingConfig.DEFAULT_STOP_LOSS = 0.3
|
| 188 |
+
TradingConfig.DEFAULT_TAKE_PROFIT = 0.6
|
| 189 |
+
TradingConfig.MIN_RISK_REWARD = 2.0
|
| 190 |
+
|
| 191 |
+
# Estratégia agressiva
|
| 192 |
+
TradingConfig.DEFAULT_STOP_LOSS = 0.8
|
| 193 |
+
TradingConfig.DEFAULT_TAKE_PROFIT = 1.5
|
| 194 |
+
TradingConfig.MIN_RISK_REWARD = 1.2
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
## 🎨 Configuração da Interface
|
| 198 |
+
|
| 199 |
+
### UIConfig
|
| 200 |
+
|
| 201 |
+
```python
|
| 202 |
+
class UIConfig:
|
| 203 |
+
"""Configurações da interface do usuário."""
|
| 204 |
+
|
| 205 |
+
# Informações do aplicativo
|
| 206 |
+
TITLE = "📈 Vampire Trading Bot"
|
| 207 |
+
SUBTITLE = "Análise Avançada de Mercado com IA"
|
| 208 |
+
DESCRIPTION = "Sistema completo de análise técnica e sentimento"
|
| 209 |
+
|
| 210 |
+
# Emojis para diferentes ações
|
| 211 |
+
ACTION_EMOJIS = {
|
| 212 |
+
"COMPRAR": "🟢",
|
| 213 |
+
"VENDER": "🔴",
|
| 214 |
+
"AGUARDAR": "🟡",
|
| 215 |
+
"NEUTRO": "⚪"
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
# Cores para diferentes elementos
|
| 219 |
+
COLORS = {
|
| 220 |
+
"success": "#28a745",
|
| 221 |
+
"danger": "#dc3545",
|
| 222 |
+
"warning": "#ffc107",
|
| 223 |
+
"info": "#17a2b8",
|
| 224 |
+
"primary": "#007bff"
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
# Mensagens de status
|
| 228 |
+
STATUS_MESSAGES = {
|
| 229 |
+
"loading": "🔄 Processando análise...",
|
| 230 |
+
"success": "✅ Análise concluída com sucesso!",
|
| 231 |
+
"error": "❌ Erro durante a análise",
|
| 232 |
+
"no_data": "⚠️ Nenhum dado fornecido"
|
| 233 |
+
}
|
| 234 |
+
```
|
| 235 |
+
|
| 236 |
+
#### Personalização da Interface
|
| 237 |
+
|
| 238 |
+
```python
|
| 239 |
+
# Tema escuro
|
| 240 |
+
UIConfig.COLORS = {
|
| 241 |
+
"success": "#00ff88",
|
| 242 |
+
"danger": "#ff4444",
|
| 243 |
+
"warning": "#ffaa00",
|
| 244 |
+
"info": "#00aaff",
|
| 245 |
+
"primary": "#8800ff"
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
# Personalizar título
|
| 249 |
+
UIConfig.TITLE = "🧛 Meu Bot de Trading"
|
| 250 |
+
UIConfig.SUBTITLE = "Análise Personalizada"
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
## 🔍 Configuração de Padrões Regex
|
| 254 |
+
|
| 255 |
+
### RegexPatterns
|
| 256 |
+
|
| 257 |
+
```python
|
| 258 |
+
class RegexPatterns:
|
| 259 |
+
"""Padrões regex para extração de dados."""
|
| 260 |
+
|
| 261 |
+
# Extração de preços
|
| 262 |
+
PRICE_PATTERN = r"Preço.*?([\d,]+\.\d+)"
|
| 263 |
+
|
| 264 |
+
# Extração de variação
|
| 265 |
+
VARIATION_PATTERN = r"Variação.*?([+-]?[\d,]+\.\d+)"
|
| 266 |
+
|
| 267 |
+
# Extração de porcentagem
|
| 268 |
+
PERCENTAGE_PATTERN = r"\(([+-]?\d+\.\d+)%\)"
|
| 269 |
+
|
| 270 |
+
# Extração de volume
|
| 271 |
+
VOLUME_PATTERN = r"Volume.*?(\d+)"
|
| 272 |
+
|
| 273 |
+
# Extração de RSI
|
| 274 |
+
RSI_PATTERN = r"RSI.*?(\d+\.\d+)"
|
| 275 |
+
|
| 276 |
+
# Extração de EMAs
|
| 277 |
+
EMA_PATTERN = r"EMA.*?(\d+\.\d+)"
|
| 278 |
+
```
|
| 279 |
+
|
| 280 |
+
#### Personalização de Padrões
|
| 281 |
+
|
| 282 |
+
```python
|
| 283 |
+
# Padrões para formato brasileiro
|
| 284 |
+
RegexPatterns.PRICE_PATTERN = r"R\$\s*([\d.]+,\d+)"
|
| 285 |
+
RegexPatterns.PERCENTAGE_PATTERN = r"\(([+-]?\d+,\d+)%\)"
|
| 286 |
+
|
| 287 |
+
# Padrões para outros idiomas
|
| 288 |
+
RegexPatterns.PRICE_PATTERN = r"Price.*?([\d,]+\.\d+)"
|
| 289 |
+
```
|
| 290 |
+
|
| 291 |
+
## 📈 Configuração de Pontuação
|
| 292 |
+
|
| 293 |
+
### ScoringConfig
|
| 294 |
+
|
| 295 |
+
```python
|
| 296 |
+
class ScoringConfig:
|
| 297 |
+
"""Configurações de pontuação e confiança."""
|
| 298 |
+
|
| 299 |
+
# Pesos para diferentes análises
|
| 300 |
+
TECHNICAL_WEIGHT = 0.4
|
| 301 |
+
SENTIMENT_WEIGHT = 0.3
|
| 302 |
+
FIBONACCI_WEIGHT = 0.3
|
| 303 |
+
|
| 304 |
+
# Thresholds de confiança
|
| 305 |
+
HIGH_CONFIDENCE_THRESHOLD = 80
|
| 306 |
+
MEDIUM_CONFIDENCE_THRESHOLD = 60
|
| 307 |
+
LOW_CONFIDENCE_THRESHOLD = 40
|
| 308 |
+
|
| 309 |
+
# Multiplicadores de pontuação
|
| 310 |
+
STRONG_SIGNAL_MULTIPLIER = 1.5
|
| 311 |
+
WEAK_SIGNAL_MULTIPLIER = 0.7
|
| 312 |
+
|
| 313 |
+
# Penalizações
|
| 314 |
+
CONFLICTING_SIGNALS_PENALTY = 0.2
|
| 315 |
+
LOW_VOLUME_PENALTY = 0.1
|
| 316 |
+
```
|
| 317 |
+
|
| 318 |
+
#### Ajuste de Pesos
|
| 319 |
+
|
| 320 |
+
```python
|
| 321 |
+
# Priorizar análise técnica
|
| 322 |
+
ScoringConfig.TECHNICAL_WEIGHT = 0.6
|
| 323 |
+
ScoringConfig.SENTIMENT_WEIGHT = 0.2
|
| 324 |
+
ScoringConfig.FIBONACCI_WEIGHT = 0.2
|
| 325 |
+
|
| 326 |
+
# Priorizar sentimento
|
| 327 |
+
ScoringConfig.TECHNICAL_WEIGHT = 0.2
|
| 328 |
+
ScoringConfig.SENTIMENT_WEIGHT = 0.6
|
| 329 |
+
ScoringConfig.FIBONACCI_WEIGHT = 0.2
|
| 330 |
+
```
|
| 331 |
+
|
| 332 |
+
## 🚀 Configuração de Performance
|
| 333 |
+
|
| 334 |
+
### Configurações de Sistema
|
| 335 |
+
|
| 336 |
+
```python
|
| 337 |
+
# Em performance_monitor.py
|
| 338 |
+
class PerformanceConfig:
|
| 339 |
+
# Intervalo de coleta de métricas (segundos)
|
| 340 |
+
METRICS_INTERVAL = 5.0
|
| 341 |
+
|
| 342 |
+
# Histórico máximo de métricas
|
| 343 |
+
MAX_METRICS_HISTORY = 1000
|
| 344 |
+
|
| 345 |
+
# Thresholds de alerta
|
| 346 |
+
CPU_ALERT_THRESHOLD = 80.0
|
| 347 |
+
MEMORY_ALERT_THRESHOLD = 85.0
|
| 348 |
+
|
| 349 |
+
# Configurações de otimização
|
| 350 |
+
ENABLE_AUTO_OPTIMIZATION = True
|
| 351 |
+
OPTIMIZATION_INTERVAL = 300 # 5 minutos
|
| 352 |
+
```
|
| 353 |
+
|
| 354 |
+
### Configurações de Cache
|
| 355 |
+
|
| 356 |
+
```python
|
| 357 |
+
# Cache de modelos
|
| 358 |
+
MODEL_CACHE_SIZE = 3
|
| 359 |
+
MODEL_CACHE_TTL = 3600 # 1 hora
|
| 360 |
+
|
| 361 |
+
# Cache de análises
|
| 362 |
+
ANALYSIS_CACHE_SIZE = 100
|
| 363 |
+
ANALYSIS_CACHE_TTL = 300 # 5 minutos
|
| 364 |
+
```
|
| 365 |
+
|
| 366 |
+
## 🔧 Configuração via Variáveis de Ambiente
|
| 367 |
+
|
| 368 |
+
### Variáveis Suportadas
|
| 369 |
+
|
| 370 |
+
```bash
|
| 371 |
+
# Modelo de IA
|
| 372 |
+
export FINBERT_MODEL="ProsusAI/finbert"
|
| 373 |
+
export MAX_TEXT_LENGTH="512"
|
| 374 |
+
|
| 375 |
+
# Performance
|
| 376 |
+
export MAX_WORKERS="4"
|
| 377 |
+
export ENABLE_GPU="true"
|
| 378 |
+
export CACHE_SIZE="1000"
|
| 379 |
+
|
| 380 |
+
# Logging
|
| 381 |
+
export LOG_LEVEL="INFO"
|
| 382 |
+
export LOG_FILE="vampire_bot.log"
|
| 383 |
+
|
| 384 |
+
# Interface
|
| 385 |
+
export GRADIO_SERVER_NAME="0.0.0.0"
|
| 386 |
+
export GRADIO_SERVER_PORT="7860"
|
| 387 |
+
export GRADIO_SHARE="false"
|
| 388 |
+
|
| 389 |
+
# Real-time
|
| 390 |
+
export LOG_FILE_PATH="/path/to/trading.log"
|
| 391 |
+
export CHECK_INTERVAL="1.0"
|
| 392 |
+
```
|
| 393 |
+
|
| 394 |
+
### Carregamento de Variáveis
|
| 395 |
+
|
| 396 |
+
```python
|
| 397 |
+
import os
|
| 398 |
+
from config import AIConfig, UIConfig
|
| 399 |
+
|
| 400 |
+
# Carregar configurações do ambiente
|
| 401 |
+
AIConfig.MAX_TEXT_LENGTH = int(os.getenv('MAX_TEXT_LENGTH', '512'))
|
| 402 |
+
AIConfig.USE_GPU = os.getenv('ENABLE_GPU', 'true').lower() == 'true'
|
| 403 |
+
|
| 404 |
+
UIConfig.TITLE = os.getenv('APP_TITLE', UIConfig.TITLE)
|
| 405 |
+
```
|
| 406 |
+
|
| 407 |
+
## 📋 Configuração por Perfil
|
| 408 |
+
|
| 409 |
+
### Perfis Pré-definidos
|
| 410 |
+
|
| 411 |
+
```python
|
| 412 |
+
# profiles.py
|
| 413 |
+
class DevelopmentProfile:
|
| 414 |
+
"""Perfil para desenvolvimento."""
|
| 415 |
+
DEBUG = True
|
| 416 |
+
LOG_LEVEL = "DEBUG"
|
| 417 |
+
ENABLE_MODEL_CACHE = False
|
| 418 |
+
USE_LIGHTWEIGHT_MODELS = True
|
| 419 |
+
|
| 420 |
+
class ProductionProfile:
|
| 421 |
+
"""Perfil para produção."""
|
| 422 |
+
DEBUG = False
|
| 423 |
+
LOG_LEVEL = "INFO"
|
| 424 |
+
ENABLE_MODEL_CACHE = True
|
| 425 |
+
USE_LIGHTWEIGHT_MODELS = False
|
| 426 |
+
ENABLE_MONITORING = True
|
| 427 |
+
|
| 428 |
+
class TestingProfile:
|
| 429 |
+
"""Perfil para testes."""
|
| 430 |
+
DEBUG = True
|
| 431 |
+
LOG_LEVEL = "WARNING"
|
| 432 |
+
USE_MOCK_MODELS = True
|
| 433 |
+
DISABLE_REAL_TIME = True
|
| 434 |
+
```
|
| 435 |
+
|
| 436 |
+
### Aplicação de Perfis
|
| 437 |
+
|
| 438 |
+
```python
|
| 439 |
+
# app.py
|
| 440 |
+
import os
|
| 441 |
+
from profiles import DevelopmentProfile, ProductionProfile
|
| 442 |
+
|
| 443 |
+
profile = os.getenv('APP_PROFILE', 'development')
|
| 444 |
+
|
| 445 |
+
if profile == 'production':
|
| 446 |
+
config = ProductionProfile()
|
| 447 |
+
else:
|
| 448 |
+
config = DevelopmentProfile()
|
| 449 |
+
|
| 450 |
+
# Aplicar configurações
|
| 451 |
+
for attr in dir(config):
|
| 452 |
+
if not attr.startswith('_'):
|
| 453 |
+
setattr(globals()[attr.split('_')[0] + 'Config'], attr, getattr(config, attr))
|
| 454 |
+
```
|
| 455 |
+
|
| 456 |
+
## 🔄 Configuração Dinâmica
|
| 457 |
+
|
| 458 |
+
### Recarregamento de Configuração
|
| 459 |
+
|
| 460 |
+
```python
|
| 461 |
+
class ConfigManager:
|
| 462 |
+
"""Gerenciador de configuração dinâmica."""
|
| 463 |
+
|
| 464 |
+
@staticmethod
|
| 465 |
+
def reload_config():
|
| 466 |
+
"""Recarrega configurações do arquivo."""
|
| 467 |
+
importlib.reload(config)
|
| 468 |
+
|
| 469 |
+
@staticmethod
|
| 470 |
+
def update_config(section: str, key: str, value: Any):
|
| 471 |
+
"""Atualiza configuração específica."""
|
| 472 |
+
config_class = getattr(config, section)
|
| 473 |
+
setattr(config_class, key, value)
|
| 474 |
+
|
| 475 |
+
@staticmethod
|
| 476 |
+
def get_config(section: str, key: str) -> Any:
|
| 477 |
+
"""Obtém valor de configuração."""
|
| 478 |
+
config_class = getattr(config, section)
|
| 479 |
+
return getattr(config_class, key)
|
| 480 |
+
```
|
| 481 |
+
|
| 482 |
+
### Interface de Configuração
|
| 483 |
+
|
| 484 |
+
```python
|
| 485 |
+
# Adicionar à interface Gradio
|
| 486 |
+
def create_config_interface():
|
| 487 |
+
with gr.Tab("⚙️ Configurações"):
|
| 488 |
+
with gr.Row():
|
| 489 |
+
model_dropdown = gr.Dropdown(
|
| 490 |
+
choices=FinancialModels.FINANCIAL_MODELS,
|
| 491 |
+
value=FinancialModels.DEFAULT_MODEL,
|
| 492 |
+
label="Modelo de IA"
|
| 493 |
+
)
|
| 494 |
+
|
| 495 |
+
with gr.Row():
|
| 496 |
+
rsi_period = gr.Number(
|
| 497 |
+
value=TechnicalAnalysis.RSI_PERIOD,
|
| 498 |
+
label="Período RSI"
|
| 499 |
+
)
|
| 500 |
+
|
| 501 |
+
update_btn = gr.Button("Atualizar Configurações")
|
| 502 |
+
|
| 503 |
+
update_btn.click(
|
| 504 |
+
fn=update_configurations,
|
| 505 |
+
inputs=[model_dropdown, rsi_period],
|
| 506 |
+
outputs=[]
|
| 507 |
+
)
|
| 508 |
+
```
|
| 509 |
+
|
| 510 |
+
## 📝 Exemplos de Configuração
|
| 511 |
+
|
| 512 |
+
### Configuração Básica
|
| 513 |
+
|
| 514 |
+
```python
|
| 515 |
+
# config_basic.py
|
| 516 |
+
from config import *
|
| 517 |
+
|
| 518 |
+
# Usar modelo leve
|
| 519 |
+
FinancialModels.DEFAULT_MODEL = "nlptown/bert-base-multilingual-uncased-sentiment"
|
| 520 |
+
|
| 521 |
+
# Configurações conservadoras
|
| 522 |
+
TradingConfig.DEFAULT_STOP_LOSS = 0.3
|
| 523 |
+
TradingConfig.MIN_RISK_REWARD = 2.0
|
| 524 |
+
|
| 525 |
+
# Interface simples
|
| 526 |
+
UIConfig.TITLE = "Bot de Trading Simples"
|
| 527 |
+
```
|
| 528 |
+
|
| 529 |
+
### Configuração Avançada
|
| 530 |
+
|
| 531 |
+
```python
|
| 532 |
+
# config_advanced.py
|
| 533 |
+
from config import *
|
| 534 |
+
|
| 535 |
+
# Usar modelo mais preciso
|
| 536 |
+
FinancialModels.DEFAULT_MODEL = "ProsusAI/finbert"
|
| 537 |
+
|
| 538 |
+
# Análise técnica mais sensível
|
| 539 |
+
TechnicalAnalysis.RSI_PERIOD = 10
|
| 540 |
+
TechnicalAnalysis.EMA_FAST_PERIOD = 5
|
| 541 |
+
|
| 542 |
+
# Trading mais agressivo
|
| 543 |
+
TradingConfig.DEFAULT_STOP_LOSS = 0.8
|
| 544 |
+
TradingConfig.MIN_RISK_REWARD = 1.2
|
| 545 |
+
|
| 546 |
+
# Performance otimizada
|
| 547 |
+
AIConfig.BATCH_SIZE = 16
|
| 548 |
+
AIConfig.ENABLE_MODEL_CACHE = True
|
| 549 |
+
```
|
| 550 |
+
|
| 551 |
+
## 🔍 Troubleshooting de Configuração
|
| 552 |
+
|
| 553 |
+
### Problemas Comuns
|
| 554 |
+
|
| 555 |
+
1. **Modelo não carrega**:
|
| 556 |
+
```python
|
| 557 |
+
# Verificar se modelo existe
|
| 558 |
+
try:
|
| 559 |
+
from transformers import pipeline
|
| 560 |
+
pipeline("sentiment-analysis", model=FinancialModels.DEFAULT_MODEL)
|
| 561 |
+
except Exception as e:
|
| 562 |
+
print(f"Erro ao carregar modelo: {e}")
|
| 563 |
+
```
|
| 564 |
+
|
| 565 |
+
2. **Configuração não aplicada**:
|
| 566 |
+
```python
|
| 567 |
+
# Verificar se configuração foi carregada
|
| 568 |
+
print(f"Modelo atual: {FinancialModels.DEFAULT_MODEL}")
|
| 569 |
+
print(f"RSI período: {TechnicalAnalysis.RSI_PERIOD}")
|
| 570 |
+
```
|
| 571 |
+
|
| 572 |
+
3. **Performance baixa**:
|
| 573 |
+
```python
|
| 574 |
+
# Configurações para melhor performance
|
| 575 |
+
AIConfig.MAX_TEXT_LENGTH = 256
|
| 576 |
+
AIConfig.BATCH_SIZE = 4
|
| 577 |
+
AIConfig.USE_GPU = False
|
| 578 |
+
```
|
| 579 |
+
|
| 580 |
+
### Validação de Configuração
|
| 581 |
+
|
| 582 |
+
```python
|
| 583 |
+
def validate_config():
|
| 584 |
+
"""Valida configurações atuais."""
|
| 585 |
+
errors = []
|
| 586 |
+
|
| 587 |
+
# Validar modelo
|
| 588 |
+
if FinancialModels.DEFAULT_MODEL not in FinancialModels.FINANCIAL_MODELS:
|
| 589 |
+
errors.append("Modelo padrão não está na lista de modelos")
|
| 590 |
+
|
| 591 |
+
# Validar períodos
|
| 592 |
+
if TechnicalAnalysis.RSI_PERIOD < 2:
|
| 593 |
+
errors.append("Período RSI muito baixo")
|
| 594 |
+
|
| 595 |
+
# Validar trading
|
| 596 |
+
if TradingConfig.DEFAULT_STOP_LOSS <= 0:
|
| 597 |
+
errors.append("Stop loss deve ser positivo")
|
| 598 |
+
|
| 599 |
+
return errors
|
| 600 |
+
```
|
| 601 |
+
|
| 602 |
+
## 📚 Próximos Passos
|
| 603 |
+
|
| 604 |
+
- Leia o [Guia do Desenvolvedor](developer-guide.md)
|
| 605 |
+
- Consulte a [Referência da API](api-reference.md)
|
| 606 |
+
- Veja exemplos em [Troubleshooting](troubleshooting.md)
|
docs/developer-guide.md
ADDED
|
@@ -0,0 +1,963 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 👨💻 Guia do Desenvolvedor
|
| 2 |
+
|
| 3 |
+
## Visão Geral
|
| 4 |
+
|
| 5 |
+
Este guia fornece informações essenciais para desenvolvedores que desejam contribuir ou estender o Vampire Trading Bot. Inclui padrões de código, arquitetura, fluxos de desenvolvimento e melhores práticas.
|
| 6 |
+
|
| 7 |
+
## 🏗️ Arquitetura do Sistema
|
| 8 |
+
|
| 9 |
+
### Estrutura Modular
|
| 10 |
+
|
| 11 |
+
```
|
| 12 |
+
vampire-trading-bot/
|
| 13 |
+
├── app.py # Ponto de entrada principal
|
| 14 |
+
├── config.py # Configurações centralizadas
|
| 15 |
+
├── ui.py # Interface Gradio
|
| 16 |
+
├── market_analysis.py # Análise técnica
|
| 17 |
+
├── sentiment_analysis.py # Análise de sentimento
|
| 18 |
+
├── fibonacci_analysis.py # Análise de Fibonacci
|
| 19 |
+
├── log_parser.py # Parser de logs
|
| 20 |
+
├── real_time_integration.py # Integração tempo real
|
| 21 |
+
├── performance_monitor.py # Monitoramento
|
| 22 |
+
├── utils.py # Utilitários
|
| 23 |
+
├── docs/ # Documentação
|
| 24 |
+
└── tests/ # Testes automatizados
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
### Princípios de Design
|
| 28 |
+
|
| 29 |
+
1. **Separação de Responsabilidades**: Cada módulo tem uma função específica
|
| 30 |
+
2. **Baixo Acoplamento**: Módulos independentes com interfaces bem definidas
|
| 31 |
+
3. **Alta Coesão**: Funcionalidades relacionadas agrupadas
|
| 32 |
+
4. **Extensibilidade**: Fácil adição de novos recursos
|
| 33 |
+
5. **Testabilidade**: Código facilmente testável
|
| 34 |
+
|
| 35 |
+
## 🔧 Configuração do Ambiente de Desenvolvimento
|
| 36 |
+
|
| 37 |
+
### Pré-requisitos
|
| 38 |
+
|
| 39 |
+
```bash
|
| 40 |
+
# Python 3.8+
|
| 41 |
+
python --version
|
| 42 |
+
|
| 43 |
+
# Git
|
| 44 |
+
git --version
|
| 45 |
+
|
| 46 |
+
# Editor recomendado: VS Code com extensões Python
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
### Setup Inicial
|
| 50 |
+
|
| 51 |
+
```bash
|
| 52 |
+
# 1. Clonar repositório
|
| 53 |
+
git clone <repository-url>
|
| 54 |
+
cd vampire-trading-bot
|
| 55 |
+
|
| 56 |
+
# 2. Criar ambiente virtual
|
| 57 |
+
python -m venv venv
|
| 58 |
+
|
| 59 |
+
# 3. Ativar ambiente
|
| 60 |
+
# Windows
|
| 61 |
+
venv\Scripts\activate
|
| 62 |
+
# Linux/macOS
|
| 63 |
+
source venv/bin/activate
|
| 64 |
+
|
| 65 |
+
# 4. Instalar dependências de desenvolvimento
|
| 66 |
+
pip install -r requirements-dev.txt
|
| 67 |
+
|
| 68 |
+
# 5. Instalar hooks de pre-commit
|
| 69 |
+
pre-commit install
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
### Dependências de Desenvolvimento
|
| 73 |
+
|
| 74 |
+
```txt
|
| 75 |
+
# requirements-dev.txt
|
| 76 |
+
pytest>=7.0.0
|
| 77 |
+
pytest-cov>=4.0.0
|
| 78 |
+
black>=22.0.0
|
| 79 |
+
flake8>=5.0.0
|
| 80 |
+
mypy>=1.0.0
|
| 81 |
+
pre-commit>=2.20.0
|
| 82 |
+
bandit>=1.7.0
|
| 83 |
+
isort>=5.10.0
|
| 84 |
+
memory-profiler>=0.60.0
|
| 85 |
+
line-profiler>=4.0.0
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
## 📝 Padrões de Código
|
| 89 |
+
|
| 90 |
+
### Estilo de Código
|
| 91 |
+
|
| 92 |
+
**Formatação com Black**:
|
| 93 |
+
```bash
|
| 94 |
+
# Formatar código
|
| 95 |
+
black .
|
| 96 |
+
|
| 97 |
+
# Verificar formatação
|
| 98 |
+
black --check .
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
**Linting com Flake8**:
|
| 102 |
+
```bash
|
| 103 |
+
# Verificar código
|
| 104 |
+
flake8 .
|
| 105 |
+
|
| 106 |
+
# Configuração em .flake8
|
| 107 |
+
[flake8]
|
| 108 |
+
max-line-length = 88
|
| 109 |
+
ignore = E203, W503
|
| 110 |
+
exclude = venv, __pycache__
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
**Ordenação de Imports com isort**:
|
| 114 |
+
```bash
|
| 115 |
+
# Ordenar imports
|
| 116 |
+
isort .
|
| 117 |
+
|
| 118 |
+
# Configuração em pyproject.toml
|
| 119 |
+
[tool.isort]
|
| 120 |
+
profile = "black"
|
| 121 |
+
line_length = 88
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
### Convenções de Nomenclatura
|
| 125 |
+
|
| 126 |
+
```python
|
| 127 |
+
# Classes: PascalCase
|
| 128 |
+
class TechnicalAnalysisEngine:
|
| 129 |
+
pass
|
| 130 |
+
|
| 131 |
+
# Funções e variáveis: snake_case
|
| 132 |
+
def calculate_rsi_value(prices: List[float]) -> float:
|
| 133 |
+
pass
|
| 134 |
+
|
| 135 |
+
# Constantes: UPPER_SNAKE_CASE
|
| 136 |
+
DEFAULT_RSI_PERIOD = 14
|
| 137 |
+
MAX_RETRY_ATTEMPTS = 3
|
| 138 |
+
|
| 139 |
+
# Arquivos: snake_case
|
| 140 |
+
# market_analysis.py
|
| 141 |
+
# sentiment_analysis.py
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
### Documentação de Código
|
| 145 |
+
|
| 146 |
+
```python
|
| 147 |
+
def analyze_market_data(
|
| 148 |
+
self,
|
| 149 |
+
text: str,
|
| 150 |
+
include_fibonacci: bool = True
|
| 151 |
+
) -> Dict[str, Any]:
|
| 152 |
+
"""
|
| 153 |
+
Analisa dados de mercado extraindo indicadores técnicos.
|
| 154 |
+
|
| 155 |
+
Args:
|
| 156 |
+
text: Texto contendo dados de mercado
|
| 157 |
+
include_fibonacci: Se deve incluir análise de Fibonacci
|
| 158 |
+
|
| 159 |
+
Returns:
|
| 160 |
+
Dict contendo:
|
| 161 |
+
- technical_signals: Lista de sinais técnicos
|
| 162 |
+
- market_data: Dados extraídos do mercado
|
| 163 |
+
- fibonacci_analysis: Análise de Fibonacci (se habilitada)
|
| 164 |
+
|
| 165 |
+
Raises:
|
| 166 |
+
ValueError: Se o texto não contém dados válidos
|
| 167 |
+
RuntimeError: Se falha na análise técnica
|
| 168 |
+
|
| 169 |
+
Example:
|
| 170 |
+
>>> engine = TechnicalAnalysisEngine()
|
| 171 |
+
>>> result = engine.analyze_market_data("WINV25: 140135")
|
| 172 |
+
>>> print(result['technical_signals'])
|
| 173 |
+
"""
|
| 174 |
+
pass
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
### Type Hints
|
| 178 |
+
|
| 179 |
+
```python
|
| 180 |
+
from typing import List, Dict, Optional, Union, Tuple, Any
|
| 181 |
+
from dataclasses import dataclass
|
| 182 |
+
from enum import Enum
|
| 183 |
+
|
| 184 |
+
# Usar type hints em todas as funções
|
| 185 |
+
def calculate_ema(
|
| 186 |
+
prices: List[float],
|
| 187 |
+
period: int
|
| 188 |
+
) -> List[float]:
|
| 189 |
+
pass
|
| 190 |
+
|
| 191 |
+
# Dataclasses para estruturas de dados
|
| 192 |
+
@dataclass
|
| 193 |
+
class MarketData:
|
| 194 |
+
symbol: str
|
| 195 |
+
price: float
|
| 196 |
+
volume: int
|
| 197 |
+
timestamp: Optional[str] = None
|
| 198 |
+
|
| 199 |
+
# Enums para constantes
|
| 200 |
+
class SignalType(Enum):
|
| 201 |
+
BUY = "buy"
|
| 202 |
+
SELL = "sell"
|
| 203 |
+
HOLD = "hold"
|
| 204 |
+
```
|
| 205 |
+
|
| 206 |
+
## 🧪 Testes
|
| 207 |
+
|
| 208 |
+
### Estrutura de Testes
|
| 209 |
+
|
| 210 |
+
```
|
| 211 |
+
tests/
|
| 212 |
+
├── __init__.py
|
| 213 |
+
├── conftest.py # Fixtures compartilhadas
|
| 214 |
+
├── test_market_analysis.py # Testes análise técnica
|
| 215 |
+
├── test_sentiment_analysis.py # Testes análise sentimento
|
| 216 |
+
├── test_fibonacci_analysis.py # Testes Fibonacci
|
| 217 |
+
├── test_integration.py # Testes integração
|
| 218 |
+
├── test_performance.py # Testes performance
|
| 219 |
+
└── fixtures/ # Dados de teste
|
| 220 |
+
├── sample_logs.txt
|
| 221 |
+
└── market_data.json
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
### Escrevendo Testes
|
| 225 |
+
|
| 226 |
+
```python
|
| 227 |
+
# test_market_analysis.py
|
| 228 |
+
import pytest
|
| 229 |
+
from unittest.mock import Mock, patch
|
| 230 |
+
from market_analysis import TechnicalAnalysisEngine, MarketData
|
| 231 |
+
|
| 232 |
+
class TestTechnicalAnalysisEngine:
|
| 233 |
+
|
| 234 |
+
@pytest.fixture
|
| 235 |
+
def engine(self):
|
| 236 |
+
"""Fixture para engine de análise."""
|
| 237 |
+
return TechnicalAnalysisEngine()
|
| 238 |
+
|
| 239 |
+
@pytest.fixture
|
| 240 |
+
def sample_market_data(self):
|
| 241 |
+
"""Fixture para dados de mercado."""
|
| 242 |
+
return MarketData(
|
| 243 |
+
symbol="WINV25",
|
| 244 |
+
price=140135.0,
|
| 245 |
+
volume=5023
|
| 246 |
+
)
|
| 247 |
+
|
| 248 |
+
def test_analyze_market_data_success(self, engine, sample_market_data):
|
| 249 |
+
"""Testa análise bem-sucedida de dados de mercado."""
|
| 250 |
+
text = "WINV25: 140135, Volume: 5023"
|
| 251 |
+
|
| 252 |
+
result = engine.analyze_market_data(text)
|
| 253 |
+
|
| 254 |
+
assert "technical_signals" in result
|
| 255 |
+
assert "market_data" in result
|
| 256 |
+
assert len(result["technical_signals"]) > 0
|
| 257 |
+
|
| 258 |
+
def test_analyze_market_data_invalid_input(self, engine):
|
| 259 |
+
"""Testa comportamento com entrada inválida."""
|
| 260 |
+
with pytest.raises(ValueError, match="Dados inválidos"):
|
| 261 |
+
engine.analyze_market_data("")
|
| 262 |
+
|
| 263 |
+
@patch('market_analysis.calculate_rsi')
|
| 264 |
+
def test_rsi_calculation_called(self, mock_rsi, engine):
|
| 265 |
+
"""Testa se cálculo RSI é chamado."""
|
| 266 |
+
mock_rsi.return_value = 65.5
|
| 267 |
+
text = "WINV25: 140135"
|
| 268 |
+
|
| 269 |
+
engine.analyze_market_data(text)
|
| 270 |
+
|
| 271 |
+
mock_rsi.assert_called_once()
|
| 272 |
+
|
| 273 |
+
@pytest.mark.parametrize("price,expected_signal", [
|
| 274 |
+
(140000, "buy"),
|
| 275 |
+
(145000, "sell"),
|
| 276 |
+
(142500, "hold")
|
| 277 |
+
])
|
| 278 |
+
def test_signal_generation(self, engine, price, expected_signal):
|
| 279 |
+
"""Testa geração de sinais para diferentes preços."""
|
| 280 |
+
text = f"WINV25: {price}"
|
| 281 |
+
|
| 282 |
+
result = engine.analyze_market_data(text)
|
| 283 |
+
signal = result["technical_signals"][0]
|
| 284 |
+
|
| 285 |
+
assert signal["action"] == expected_signal
|
| 286 |
+
```
|
| 287 |
+
|
| 288 |
+
### Executando Testes
|
| 289 |
+
|
| 290 |
+
```bash
|
| 291 |
+
# Executar todos os testes
|
| 292 |
+
pytest
|
| 293 |
+
|
| 294 |
+
# Executar com cobertura
|
| 295 |
+
pytest --cov=. --cov-report=html
|
| 296 |
+
|
| 297 |
+
# Executar testes específicos
|
| 298 |
+
pytest tests/test_market_analysis.py::TestTechnicalAnalysisEngine::test_analyze_market_data_success
|
| 299 |
+
|
| 300 |
+
# Executar testes em paralelo
|
| 301 |
+
pytest -n auto
|
| 302 |
+
|
| 303 |
+
# Executar apenas testes rápidos
|
| 304 |
+
pytest -m "not slow"
|
| 305 |
+
```
|
| 306 |
+
|
| 307 |
+
### Fixtures Compartilhadas
|
| 308 |
+
|
| 309 |
+
```python
|
| 310 |
+
# conftest.py
|
| 311 |
+
import pytest
|
| 312 |
+
from pathlib import Path
|
| 313 |
+
|
| 314 |
+
@pytest.fixture
|
| 315 |
+
def sample_log_content():
|
| 316 |
+
"""Conteúdo de log de exemplo."""
|
| 317 |
+
return """
|
| 318 |
+
⏰ Análise #1 - 09:46:58
|
| 319 |
+
📊 DADOS DE MERCADO - WINV25
|
| 320 |
+
Preço Atual: 140135.00000 ↗
|
| 321 |
+
Variação: +5 (+0.00%)
|
| 322 |
+
Volume: 5023
|
| 323 |
+
"""
|
| 324 |
+
|
| 325 |
+
@pytest.fixture
|
| 326 |
+
def temp_log_file(tmp_path, sample_log_content):
|
| 327 |
+
"""Arquivo de log temporário."""
|
| 328 |
+
log_file = tmp_path / "test_log.txt"
|
| 329 |
+
log_file.write_text(sample_log_content)
|
| 330 |
+
return log_file
|
| 331 |
+
|
| 332 |
+
@pytest.fixture
|
| 333 |
+
def mock_ai_model():
|
| 334 |
+
"""Mock do modelo de IA."""
|
| 335 |
+
from unittest.mock import Mock
|
| 336 |
+
|
| 337 |
+
model = Mock()
|
| 338 |
+
model.predict.return_value = [
|
| 339 |
+
{"label": "POSITIVE", "score": 0.85}
|
| 340 |
+
]
|
| 341 |
+
return model
|
| 342 |
+
```
|
| 343 |
+
|
| 344 |
+
## 🔄 Fluxo de Desenvolvimento
|
| 345 |
+
|
| 346 |
+
### Git Workflow
|
| 347 |
+
|
| 348 |
+
```bash
|
| 349 |
+
# 1. Criar branch para feature
|
| 350 |
+
git checkout -b feature/nova-funcionalidade
|
| 351 |
+
|
| 352 |
+
# 2. Fazer commits pequenos e frequentes
|
| 353 |
+
git add .
|
| 354 |
+
git commit -m "feat: adiciona cálculo de MACD"
|
| 355 |
+
|
| 356 |
+
# 3. Push da branch
|
| 357 |
+
git push origin feature/nova-funcionalidade
|
| 358 |
+
|
| 359 |
+
# 4. Criar Pull Request
|
| 360 |
+
# 5. Code Review
|
| 361 |
+
# 6. Merge após aprovação
|
| 362 |
+
```
|
| 363 |
+
|
| 364 |
+
### Convenções de Commit
|
| 365 |
+
|
| 366 |
+
```bash
|
| 367 |
+
# Formato: tipo(escopo): descrição
|
| 368 |
+
|
| 369 |
+
# Tipos:
|
| 370 |
+
feat: nova funcionalidade
|
| 371 |
+
fix: correção de bug
|
| 372 |
+
docs: documentação
|
| 373 |
+
style: formatação
|
| 374 |
+
refactor: refatoração
|
| 375 |
+
test: testes
|
| 376 |
+
chore: tarefas de manutenção
|
| 377 |
+
|
| 378 |
+
# Exemplos:
|
| 379 |
+
feat(analysis): adiciona indicador MACD
|
| 380 |
+
fix(ui): corrige erro na interface
|
| 381 |
+
docs(api): atualiza documentação da API
|
| 382 |
+
test(fibonacci): adiciona testes para análise
|
| 383 |
+
```
|
| 384 |
+
|
| 385 |
+
### Pre-commit Hooks
|
| 386 |
+
|
| 387 |
+
```yaml
|
| 388 |
+
# .pre-commit-config.yaml
|
| 389 |
+
repos:
|
| 390 |
+
- repo: https://github.com/psf/black
|
| 391 |
+
rev: 22.10.0
|
| 392 |
+
hooks:
|
| 393 |
+
- id: black
|
| 394 |
+
language_version: python3
|
| 395 |
+
|
| 396 |
+
- repo: https://github.com/pycqa/flake8
|
| 397 |
+
rev: 5.0.4
|
| 398 |
+
hooks:
|
| 399 |
+
- id: flake8
|
| 400 |
+
|
| 401 |
+
- repo: https://github.com/pycqa/isort
|
| 402 |
+
rev: 5.10.1
|
| 403 |
+
hooks:
|
| 404 |
+
- id: isort
|
| 405 |
+
|
| 406 |
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
| 407 |
+
rev: v0.991
|
| 408 |
+
hooks:
|
| 409 |
+
- id: mypy
|
| 410 |
+
additional_dependencies: [types-all]
|
| 411 |
+
|
| 412 |
+
- repo: https://github.com/PyCQA/bandit
|
| 413 |
+
rev: 1.7.4
|
| 414 |
+
hooks:
|
| 415 |
+
- id: bandit
|
| 416 |
+
args: ['-r', '.', '-f', 'json', '-o', 'bandit-report.json']
|
| 417 |
+
```
|
| 418 |
+
|
| 419 |
+
## 🏗️ Adicionando Novas Funcionalidades
|
| 420 |
+
|
| 421 |
+
### 1. Novo Indicador Técnico
|
| 422 |
+
|
| 423 |
+
```python
|
| 424 |
+
# Em market_analysis.py
|
| 425 |
+
|
| 426 |
+
class MACDAnalyzer(BaseAnalyzer):
|
| 427 |
+
"""Analisador MACD (Moving Average Convergence Divergence)."""
|
| 428 |
+
|
| 429 |
+
def __init__(self, fast_period: int = 12, slow_period: int = 26, signal_period: int = 9):
|
| 430 |
+
self.fast_period = fast_period
|
| 431 |
+
self.slow_period = slow_period
|
| 432 |
+
self.signal_period = signal_period
|
| 433 |
+
|
| 434 |
+
def analyze(self, prices: List[float]) -> List[TechnicalSignal]:
|
| 435 |
+
"""Calcula MACD e gera sinais."""
|
| 436 |
+
if len(prices) < self.slow_period:
|
| 437 |
+
return []
|
| 438 |
+
|
| 439 |
+
# Calcular EMAs
|
| 440 |
+
ema_fast = self._calculate_ema(prices, self.fast_period)
|
| 441 |
+
ema_slow = self._calculate_ema(prices, self.slow_period)
|
| 442 |
+
|
| 443 |
+
# Calcular MACD
|
| 444 |
+
macd_line = [fast - slow for fast, slow in zip(ema_fast, ema_slow)]
|
| 445 |
+
signal_line = self._calculate_ema(macd_line, self.signal_period)
|
| 446 |
+
|
| 447 |
+
# Gerar sinais
|
| 448 |
+
signals = []
|
| 449 |
+
for i in range(1, len(macd_line)):
|
| 450 |
+
if macd_line[i] > signal_line[i] and macd_line[i-1] <= signal_line[i-1]:
|
| 451 |
+
signals.append(TechnicalSignal(
|
| 452 |
+
indicator="MACD",
|
| 453 |
+
signal_type="buy",
|
| 454 |
+
strength=0.7,
|
| 455 |
+
value=macd_line[i],
|
| 456 |
+
timestamp=datetime.now()
|
| 457 |
+
))
|
| 458 |
+
|
| 459 |
+
return signals
|
| 460 |
+
|
| 461 |
+
# Registrar no engine
|
| 462 |
+
class TechnicalAnalysisEngine:
|
| 463 |
+
def __init__(self):
|
| 464 |
+
# ... outros analisadores
|
| 465 |
+
self.macd_analyzer = MACDAnalyzer()
|
| 466 |
+
|
| 467 |
+
def analyze_market_data(self, text: str) -> Dict[str, Any]:
|
| 468 |
+
# ... análise existente
|
| 469 |
+
macd_signals = self.macd_analyzer.analyze(prices)
|
| 470 |
+
all_signals.extend(macd_signals)
|
| 471 |
+
```
|
| 472 |
+
|
| 473 |
+
### 2. Novo Modelo de IA
|
| 474 |
+
|
| 475 |
+
```python
|
| 476 |
+
# Em sentiment_analysis.py
|
| 477 |
+
|
| 478 |
+
class CustomFinancialModel:
|
| 479 |
+
"""Modelo personalizado para análise financeira."""
|
| 480 |
+
|
| 481 |
+
def __init__(self, model_path: str):
|
| 482 |
+
self.model_path = model_path
|
| 483 |
+
self.model = None
|
| 484 |
+
self.tokenizer = None
|
| 485 |
+
|
| 486 |
+
def load(self):
|
| 487 |
+
"""Carrega modelo personalizado."""
|
| 488 |
+
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
| 489 |
+
|
| 490 |
+
self.tokenizer = AutoTokenizer.from_pretrained(self.model_path)
|
| 491 |
+
self.model = AutoModelForSequenceClassification.from_pretrained(self.model_path)
|
| 492 |
+
|
| 493 |
+
def predict(self, text: str) -> Dict[str, float]:
|
| 494 |
+
"""Faz predição de sentimento."""
|
| 495 |
+
inputs = self.tokenizer(text, return_tensors="pt", truncation=True, padding=True)
|
| 496 |
+
|
| 497 |
+
with torch.no_grad():
|
| 498 |
+
outputs = self.model(**inputs)
|
| 499 |
+
probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)
|
| 500 |
+
|
| 501 |
+
return {
|
| 502 |
+
"bullish": probabilities[0][0].item(),
|
| 503 |
+
"bearish": probabilities[0][1].item(),
|
| 504 |
+
"neutral": probabilities[0][2].item()
|
| 505 |
+
}
|
| 506 |
+
|
| 507 |
+
# Registrar no ModelManager
|
| 508 |
+
class ModelManager:
|
| 509 |
+
def __init__(self):
|
| 510 |
+
# ... modelos existentes
|
| 511 |
+
self.custom_models = {
|
| 512 |
+
"financial-bert": CustomFinancialModel,
|
| 513 |
+
"trading-roberta": CustomFinancialModel
|
| 514 |
+
}
|
| 515 |
+
```
|
| 516 |
+
|
| 517 |
+
### 3. Nova Interface de Usuário
|
| 518 |
+
|
| 519 |
+
```python
|
| 520 |
+
# Em ui.py
|
| 521 |
+
|
| 522 |
+
def create_advanced_analysis_tab():
|
| 523 |
+
"""Cria aba de análise avançada."""
|
| 524 |
+
with gr.Tab("🔬 Análise Avançada"):
|
| 525 |
+
with gr.Row():
|
| 526 |
+
with gr.Column(scale=1):
|
| 527 |
+
# Controles
|
| 528 |
+
timeframe_dropdown = gr.Dropdown(
|
| 529 |
+
choices=["1m", "5m", "15m", "1h", "4h", "1d"],
|
| 530 |
+
value="5m",
|
| 531 |
+
label="📊 Timeframe"
|
| 532 |
+
)
|
| 533 |
+
|
| 534 |
+
indicators_checklist = gr.CheckboxGroup(
|
| 535 |
+
choices=["RSI", "MACD", "Bollinger", "EMA", "Fibonacci"],
|
| 536 |
+
value=["RSI", "MACD"],
|
| 537 |
+
label="📈 Indicadores"
|
| 538 |
+
)
|
| 539 |
+
|
| 540 |
+
analyze_btn = gr.Button("🚀 Analisar", variant="primary")
|
| 541 |
+
|
| 542 |
+
with gr.Column(scale=2):
|
| 543 |
+
# Resultados
|
| 544 |
+
analysis_output = gr.JSON(label="📊 Resultado da Análise")
|
| 545 |
+
chart_output = gr.Plot(label="📈 Gráfico")
|
| 546 |
+
|
| 547 |
+
# Conectar eventos
|
| 548 |
+
analyze_btn.click(
|
| 549 |
+
fn=advanced_analysis,
|
| 550 |
+
inputs=[timeframe_dropdown, indicators_checklist],
|
| 551 |
+
outputs=[analysis_output, chart_output]
|
| 552 |
+
)
|
| 553 |
+
|
| 554 |
+
return timeframe_dropdown, indicators_checklist, analyze_btn
|
| 555 |
+
|
| 556 |
+
def advanced_analysis(timeframe: str, indicators: List[str]) -> Tuple[Dict, Any]:
|
| 557 |
+
"""Executa análise avançada."""
|
| 558 |
+
# Implementar lógica de análise
|
| 559 |
+
result = {
|
| 560 |
+
"timeframe": timeframe,
|
| 561 |
+
"indicators": indicators,
|
| 562 |
+
"signals": [],
|
| 563 |
+
"confidence": 0.0
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
# Gerar gráfico
|
| 567 |
+
import plotly.graph_objects as go
|
| 568 |
+
fig = go.Figure()
|
| 569 |
+
# ... adicionar dados ao gráfico
|
| 570 |
+
|
| 571 |
+
return result, fig
|
| 572 |
+
```
|
| 573 |
+
|
| 574 |
+
## 🔍 Debugging e Profiling
|
| 575 |
+
|
| 576 |
+
### Logging para Debug
|
| 577 |
+
|
| 578 |
+
```python
|
| 579 |
+
import logging
|
| 580 |
+
from utils import LogUtils
|
| 581 |
+
|
| 582 |
+
# Configurar logging detalhado
|
| 583 |
+
logging.basicConfig(
|
| 584 |
+
level=logging.DEBUG,
|
| 585 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
| 586 |
+
handlers=[
|
| 587 |
+
logging.FileHandler('debug.log'),
|
| 588 |
+
logging.StreamHandler()
|
| 589 |
+
]
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
logger = logging.getLogger(__name__)
|
| 593 |
+
|
| 594 |
+
def analyze_market_data(self, text: str) -> Dict[str, Any]:
|
| 595 |
+
logger.debug(f"Iniciando análise para texto: {text[:100]}...")
|
| 596 |
+
|
| 597 |
+
try:
|
| 598 |
+
# Análise
|
| 599 |
+
result = self._perform_analysis(text)
|
| 600 |
+
logger.debug(f"Análise concluída: {len(result.get('signals', []))} sinais")
|
| 601 |
+
return result
|
| 602 |
+
|
| 603 |
+
except Exception as e:
|
| 604 |
+
logger.error(f"Erro na análise: {e}", exc_info=True)
|
| 605 |
+
raise
|
| 606 |
+
```
|
| 607 |
+
|
| 608 |
+
### Profiling de Performance
|
| 609 |
+
|
| 610 |
+
```python
|
| 611 |
+
# performance_profiler.py
|
| 612 |
+
import cProfile
|
| 613 |
+
import pstats
|
| 614 |
+
from functools import wraps
|
| 615 |
+
from memory_profiler import profile
|
| 616 |
+
from line_profiler import LineProfiler
|
| 617 |
+
|
| 618 |
+
def profile_time(func):
|
| 619 |
+
"""Decorator para profiling de tempo."""
|
| 620 |
+
@wraps(func)
|
| 621 |
+
def wrapper(*args, **kwargs):
|
| 622 |
+
profiler = cProfile.Profile()
|
| 623 |
+
profiler.enable()
|
| 624 |
+
|
| 625 |
+
result = func(*args, **kwargs)
|
| 626 |
+
|
| 627 |
+
profiler.disable()
|
| 628 |
+
stats = pstats.Stats(profiler)
|
| 629 |
+
stats.sort_stats('cumulative')
|
| 630 |
+
stats.print_stats(10) # Top 10 funções
|
| 631 |
+
|
| 632 |
+
return result
|
| 633 |
+
return wrapper
|
| 634 |
+
|
| 635 |
+
@profile_time
|
| 636 |
+
@profile # memory_profiler
|
| 637 |
+
def analyze_large_dataset(data):
|
| 638 |
+
"""Análise de dataset grande."""
|
| 639 |
+
# Implementação
|
| 640 |
+
pass
|
| 641 |
+
|
| 642 |
+
# Usar line_profiler
|
| 643 |
+
def profile_line_by_line():
|
| 644 |
+
profiler = LineProfiler()
|
| 645 |
+
profiler.add_function(analyze_market_data)
|
| 646 |
+
profiler.enable_by_count()
|
| 647 |
+
|
| 648 |
+
# Executar função
|
| 649 |
+
analyze_market_data(sample_text)
|
| 650 |
+
|
| 651 |
+
profiler.print_stats()
|
| 652 |
+
```
|
| 653 |
+
|
| 654 |
+
### Debugging com pdb
|
| 655 |
+
|
| 656 |
+
```python
|
| 657 |
+
import pdb
|
| 658 |
+
|
| 659 |
+
def problematic_function(data):
|
| 660 |
+
# Ponto de breakpoint
|
| 661 |
+
pdb.set_trace()
|
| 662 |
+
|
| 663 |
+
# Código para debug
|
| 664 |
+
processed_data = process_data(data)
|
| 665 |
+
|
| 666 |
+
# Breakpoint condicional
|
| 667 |
+
if len(processed_data) == 0:
|
| 668 |
+
pdb.set_trace()
|
| 669 |
+
|
| 670 |
+
return processed_data
|
| 671 |
+
|
| 672 |
+
# Comandos úteis no pdb:
|
| 673 |
+
# l - listar código
|
| 674 |
+
# n - próxima linha
|
| 675 |
+
# s - step into
|
| 676 |
+
# c - continuar
|
| 677 |
+
# p variable - imprimir variável
|
| 678 |
+
# pp variable - pretty print
|
| 679 |
+
# h - ajuda
|
| 680 |
+
```
|
| 681 |
+
|
| 682 |
+
## 📊 Monitoramento e Métricas
|
| 683 |
+
|
| 684 |
+
### Métricas Customizadas
|
| 685 |
+
|
| 686 |
+
```python
|
| 687 |
+
# custom_metrics.py
|
| 688 |
+
from dataclasses import dataclass
|
| 689 |
+
from typing import Dict, List
|
| 690 |
+
import time
|
| 691 |
+
from collections import defaultdict
|
| 692 |
+
|
| 693 |
+
@dataclass
|
| 694 |
+
class AnalysisMetrics:
|
| 695 |
+
"""Métricas de análise."""
|
| 696 |
+
total_analyses: int = 0
|
| 697 |
+
successful_analyses: int = 0
|
| 698 |
+
failed_analyses: int = 0
|
| 699 |
+
average_processing_time: float = 0.0
|
| 700 |
+
error_types: Dict[str, int] = None
|
| 701 |
+
|
| 702 |
+
def __post_init__(self):
|
| 703 |
+
if self.error_types is None:
|
| 704 |
+
self.error_types = defaultdict(int)
|
| 705 |
+
|
| 706 |
+
class MetricsCollector:
|
| 707 |
+
"""Coletor de métricas personalizado."""
|
| 708 |
+
|
| 709 |
+
def __init__(self):
|
| 710 |
+
self.metrics = AnalysisMetrics()
|
| 711 |
+
self.processing_times = []
|
| 712 |
+
|
| 713 |
+
def record_analysis_start(self) -> float:
|
| 714 |
+
"""Registra início de análise."""
|
| 715 |
+
return time.time()
|
| 716 |
+
|
| 717 |
+
def record_analysis_success(self, start_time: float):
|
| 718 |
+
"""Registra análise bem-sucedida."""
|
| 719 |
+
processing_time = time.time() - start_time
|
| 720 |
+
|
| 721 |
+
self.metrics.total_analyses += 1
|
| 722 |
+
self.metrics.successful_analyses += 1
|
| 723 |
+
self.processing_times.append(processing_time)
|
| 724 |
+
|
| 725 |
+
# Calcular média móvel
|
| 726 |
+
if len(self.processing_times) > 100:
|
| 727 |
+
self.processing_times = self.processing_times[-100:]
|
| 728 |
+
|
| 729 |
+
self.metrics.average_processing_time = sum(self.processing_times) / len(self.processing_times)
|
| 730 |
+
|
| 731 |
+
def record_analysis_failure(self, start_time: float, error_type: str):
|
| 732 |
+
"""Registra falha na análise."""
|
| 733 |
+
self.metrics.total_analyses += 1
|
| 734 |
+
self.metrics.failed_analyses += 1
|
| 735 |
+
self.metrics.error_types[error_type] += 1
|
| 736 |
+
|
| 737 |
+
def get_success_rate(self) -> float:
|
| 738 |
+
"""Calcula taxa de sucesso."""
|
| 739 |
+
if self.metrics.total_analyses == 0:
|
| 740 |
+
return 0.0
|
| 741 |
+
return self.metrics.successful_analyses / self.metrics.total_analyses
|
| 742 |
+
|
| 743 |
+
def export_metrics(self) -> Dict:
|
| 744 |
+
"""Exporta métricas para análise."""
|
| 745 |
+
return {
|
| 746 |
+
"total_analyses": self.metrics.total_analyses,
|
| 747 |
+
"success_rate": self.get_success_rate(),
|
| 748 |
+
"average_processing_time": self.metrics.average_processing_time,
|
| 749 |
+
"error_distribution": dict(self.metrics.error_types)
|
| 750 |
+
}
|
| 751 |
+
```
|
| 752 |
+
|
| 753 |
+
## 🚀 Deploy e Produção
|
| 754 |
+
|
| 755 |
+
### Configuração para Produção
|
| 756 |
+
|
| 757 |
+
```python
|
| 758 |
+
# production_config.py
|
| 759 |
+
import os
|
| 760 |
+
from dataclasses import dataclass
|
| 761 |
+
|
| 762 |
+
@dataclass
|
| 763 |
+
class ProductionConfig:
|
| 764 |
+
"""Configurações para produção."""
|
| 765 |
+
|
| 766 |
+
# Servidor
|
| 767 |
+
HOST: str = os.getenv("HOST", "0.0.0.0")
|
| 768 |
+
PORT: int = int(os.getenv("PORT", "7860"))
|
| 769 |
+
WORKERS: int = int(os.getenv("WORKERS", "4"))
|
| 770 |
+
|
| 771 |
+
# Logging
|
| 772 |
+
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
|
| 773 |
+
LOG_FILE: str = os.getenv("LOG_FILE", "app.log")
|
| 774 |
+
|
| 775 |
+
# Performance
|
| 776 |
+
MAX_CONCURRENT_ANALYSES: int = int(os.getenv("MAX_CONCURRENT_ANALYSES", "10"))
|
| 777 |
+
ANALYSIS_TIMEOUT: int = int(os.getenv("ANALYSIS_TIMEOUT", "30"))
|
| 778 |
+
|
| 779 |
+
# Segurança
|
| 780 |
+
ENABLE_AUTH: bool = os.getenv("ENABLE_AUTH", "false").lower() == "true"
|
| 781 |
+
SECRET_KEY: str = os.getenv("SECRET_KEY", "")
|
| 782 |
+
|
| 783 |
+
# Cache
|
| 784 |
+
ENABLE_REDIS: bool = os.getenv("ENABLE_REDIS", "false").lower() == "true"
|
| 785 |
+
REDIS_URL: str = os.getenv("REDIS_URL", "redis://localhost:6379")
|
| 786 |
+
```
|
| 787 |
+
|
| 788 |
+
### Docker
|
| 789 |
+
|
| 790 |
+
```dockerfile
|
| 791 |
+
# Dockerfile
|
| 792 |
+
FROM python:3.9-slim
|
| 793 |
+
|
| 794 |
+
# Instalar dependências do sistema
|
| 795 |
+
RUN apt-get update && apt-get install -y \
|
| 796 |
+
gcc \
|
| 797 |
+
g++ \
|
| 798 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 799 |
+
|
| 800 |
+
# Criar usuário não-root
|
| 801 |
+
RUN useradd --create-home --shell /bin/bash app
|
| 802 |
+
USER app
|
| 803 |
+
WORKDIR /home/app
|
| 804 |
+
|
| 805 |
+
# Copiar requirements
|
| 806 |
+
COPY --chown=app:app requirements.txt .
|
| 807 |
+
|
| 808 |
+
# Instalar dependências Python
|
| 809 |
+
RUN pip install --user --no-cache-dir -r requirements.txt
|
| 810 |
+
|
| 811 |
+
# Copiar código
|
| 812 |
+
COPY --chown=app:app . .
|
| 813 |
+
|
| 814 |
+
# Expor porta
|
| 815 |
+
EXPOSE 7860
|
| 816 |
+
|
| 817 |
+
# Comando de inicialização
|
| 818 |
+
CMD ["python", "app.py", "--host", "0.0.0.0", "--port", "7860"]
|
| 819 |
+
```
|
| 820 |
+
|
| 821 |
+
```yaml
|
| 822 |
+
# docker-compose.yml
|
| 823 |
+
version: '3.8'
|
| 824 |
+
|
| 825 |
+
services:
|
| 826 |
+
vampire-bot:
|
| 827 |
+
build: .
|
| 828 |
+
ports:
|
| 829 |
+
- "7860:7860"
|
| 830 |
+
environment:
|
| 831 |
+
- LOG_LEVEL=INFO
|
| 832 |
+
- MAX_CONCURRENT_ANALYSES=5
|
| 833 |
+
volumes:
|
| 834 |
+
- ./logs:/home/app/logs
|
| 835 |
+
- ./models:/home/app/models
|
| 836 |
+
restart: unless-stopped
|
| 837 |
+
|
| 838 |
+
redis:
|
| 839 |
+
image: redis:7-alpine
|
| 840 |
+
ports:
|
| 841 |
+
- "6379:6379"
|
| 842 |
+
volumes:
|
| 843 |
+
- redis_data:/data
|
| 844 |
+
restart: unless-stopped
|
| 845 |
+
|
| 846 |
+
volumes:
|
| 847 |
+
redis_data:
|
| 848 |
+
```
|
| 849 |
+
|
| 850 |
+
## 📚 Recursos Adicionais
|
| 851 |
+
|
| 852 |
+
### Ferramentas Recomendadas
|
| 853 |
+
|
| 854 |
+
- **IDE**: VS Code com extensões Python
|
| 855 |
+
- **Debugging**: pdb, ipdb, VS Code debugger
|
| 856 |
+
- **Profiling**: cProfile, memory_profiler, line_profiler
|
| 857 |
+
- **Testing**: pytest, coverage.py
|
| 858 |
+
- **Linting**: flake8, pylint, mypy
|
| 859 |
+
- **Formatting**: black, isort
|
| 860 |
+
- **Documentation**: Sphinx, mkdocs
|
| 861 |
+
|
| 862 |
+
### Bibliotecas Úteis
|
| 863 |
+
|
| 864 |
+
```python
|
| 865 |
+
# Análise de dados
|
| 866 |
+
import pandas as pd
|
| 867 |
+
import numpy as np
|
| 868 |
+
from scipy import stats
|
| 869 |
+
|
| 870 |
+
# Visualização
|
| 871 |
+
import matplotlib.pyplot as plt
|
| 872 |
+
import plotly.graph_objects as go
|
| 873 |
+
import seaborn as sns
|
| 874 |
+
|
| 875 |
+
# Machine Learning
|
| 876 |
+
from sklearn.metrics import accuracy_score, classification_report
|
| 877 |
+
from sklearn.model_selection import train_test_split
|
| 878 |
+
|
| 879 |
+
# Async/Concorrência
|
| 880 |
+
import asyncio
|
| 881 |
+
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
|
| 882 |
+
|
| 883 |
+
# Monitoramento
|
| 884 |
+
import psutil
|
| 885 |
+
from prometheus_client import Counter, Histogram, Gauge
|
| 886 |
+
```
|
| 887 |
+
|
| 888 |
+
### Padrões de Design Úteis
|
| 889 |
+
|
| 890 |
+
```python
|
| 891 |
+
# Strategy Pattern para diferentes análises
|
| 892 |
+
from abc import ABC, abstractmethod
|
| 893 |
+
|
| 894 |
+
class AnalysisStrategy(ABC):
|
| 895 |
+
@abstractmethod
|
| 896 |
+
def analyze(self, data) -> Dict:
|
| 897 |
+
pass
|
| 898 |
+
|
| 899 |
+
class RSIStrategy(AnalysisStrategy):
|
| 900 |
+
def analyze(self, data) -> Dict:
|
| 901 |
+
# Implementar RSI
|
| 902 |
+
pass
|
| 903 |
+
|
| 904 |
+
class MACDStrategy(AnalysisStrategy):
|
| 905 |
+
def analyze(self, data) -> Dict:
|
| 906 |
+
# Implementar MACD
|
| 907 |
+
pass
|
| 908 |
+
|
| 909 |
+
# Factory Pattern para criação de analisadores
|
| 910 |
+
class AnalyzerFactory:
|
| 911 |
+
_strategies = {
|
| 912 |
+
"rsi": RSIStrategy,
|
| 913 |
+
"macd": MACDStrategy
|
| 914 |
+
}
|
| 915 |
+
|
| 916 |
+
@classmethod
|
| 917 |
+
def create_analyzer(cls, analyzer_type: str) -> AnalysisStrategy:
|
| 918 |
+
if analyzer_type not in cls._strategies:
|
| 919 |
+
raise ValueError(f"Analisador {analyzer_type} não suportado")
|
| 920 |
+
return cls._strategies[analyzer_type]()
|
| 921 |
+
|
| 922 |
+
# Observer Pattern para notificações
|
| 923 |
+
class Observable:
|
| 924 |
+
def __init__(self):
|
| 925 |
+
self._observers = []
|
| 926 |
+
|
| 927 |
+
def attach(self, observer):
|
| 928 |
+
self._observers.append(observer)
|
| 929 |
+
|
| 930 |
+
def notify(self, event):
|
| 931 |
+
for observer in self._observers:
|
| 932 |
+
observer.update(event)
|
| 933 |
+
```
|
| 934 |
+
|
| 935 |
+
## 🤝 Contribuindo
|
| 936 |
+
|
| 937 |
+
### Processo de Contribuição
|
| 938 |
+
|
| 939 |
+
1. **Fork** do repositório
|
| 940 |
+
2. **Clone** do seu fork
|
| 941 |
+
3. **Criar branch** para feature/bugfix
|
| 942 |
+
4. **Implementar** mudanças com testes
|
| 943 |
+
5. **Executar** testes e linting
|
| 944 |
+
6. **Commit** seguindo convenções
|
| 945 |
+
7. **Push** da branch
|
| 946 |
+
8. **Criar Pull Request**
|
| 947 |
+
9. **Code Review**
|
| 948 |
+
10. **Merge** após aprovação
|
| 949 |
+
|
| 950 |
+
### Checklist para Pull Requests
|
| 951 |
+
|
| 952 |
+
- [ ] Código segue padrões de estilo
|
| 953 |
+
- [ ] Testes adicionados/atualizados
|
| 954 |
+
- [ ] Documentação atualizada
|
| 955 |
+
- [ ] Todos os testes passam
|
| 956 |
+
- [ ] Sem warnings de linting
|
| 957 |
+
- [ ] Performance não degradada
|
| 958 |
+
- [ ] Compatibilidade mantida
|
| 959 |
+
- [ ] Changelog atualizado
|
| 960 |
+
|
| 961 |
+
---
|
| 962 |
+
|
| 963 |
+
**🎯 Objetivo**: Manter código limpo, testável e bem documentado para facilitar manutenção e evolução do projeto!
|
docs/installation.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🚀 Guia de Instalação
|
| 2 |
+
|
| 3 |
+
## Pré-requisitos
|
| 4 |
+
|
| 5 |
+
### Sistema Operacional
|
| 6 |
+
- Windows 10/11 (recomendado)
|
| 7 |
+
- Linux (Ubuntu 20.04+)
|
| 8 |
+
- macOS (10.15+)
|
| 9 |
+
|
| 10 |
+
### Software Necessário
|
| 11 |
+
- **Python 3.13+** (obrigatório)
|
| 12 |
+
- **Git** (para clonagem do repositório)
|
| 13 |
+
- **Visual Studio Build Tools** (Windows - para compilação de dependências)
|
| 14 |
+
|
| 15 |
+
## Instalação Passo a Passo
|
| 16 |
+
|
| 17 |
+
### 1. Preparação do Ambiente
|
| 18 |
+
|
| 19 |
+
#### Windows
|
| 20 |
+
```powershell
|
| 21 |
+
# Verificar versão do Python
|
| 22 |
+
python --version
|
| 23 |
+
|
| 24 |
+
# Instalar Visual Studio Build Tools (se necessário)
|
| 25 |
+
# Baixar de: https://visualstudio.microsoft.com/visual-cpp-build-tools/
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
#### Linux/macOS
|
| 29 |
+
```bash
|
| 30 |
+
# Verificar versão do Python
|
| 31 |
+
python3 --version
|
| 32 |
+
|
| 33 |
+
# Instalar dependências do sistema (Ubuntu)
|
| 34 |
+
sudo apt update
|
| 35 |
+
sudo apt install python3-pip python3-venv build-essential
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
### 2. Clonagem do Repositório
|
| 39 |
+
|
| 40 |
+
```bash
|
| 41 |
+
git clone <repository-url>
|
| 42 |
+
cd vampire-trading-bot
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
### 3. Criação do Ambiente Virtual
|
| 46 |
+
|
| 47 |
+
#### Windows
|
| 48 |
+
```powershell
|
| 49 |
+
# Criar ambiente virtual
|
| 50 |
+
python -m venv venv
|
| 51 |
+
|
| 52 |
+
# Ativar ambiente virtual
|
| 53 |
+
.\venv\Scripts\Activate.ps1
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
#### Linux/macOS
|
| 57 |
+
```bash
|
| 58 |
+
# Criar ambiente virtual
|
| 59 |
+
python3 -m venv venv
|
| 60 |
+
|
| 61 |
+
# Ativar ambiente virtual
|
| 62 |
+
source venv/bin/activate
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
### 4. Instalação das Dependências
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
# Atualizar pip
|
| 69 |
+
pip install --upgrade pip
|
| 70 |
+
|
| 71 |
+
# Instalar dependências principais
|
| 72 |
+
pip install -r requirements.txt
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
#### Dependências Principais
|
| 76 |
+
- `gradio==4.44.0` - Interface web
|
| 77 |
+
- `transformers` - Modelos de IA
|
| 78 |
+
- `torch` - Framework de deep learning
|
| 79 |
+
- `numpy` - Computação numérica
|
| 80 |
+
- `pandas` - Manipulação de dados
|
| 81 |
+
- `scipy` - Computação científica
|
| 82 |
+
- `psutil` - Monitoramento de sistema
|
| 83 |
+
|
| 84 |
+
### 5. Verificação da Instalação
|
| 85 |
+
|
| 86 |
+
```bash
|
| 87 |
+
# Testar importações principais
|
| 88 |
+
python -c "import gradio, transformers, torch, numpy, pandas; print('✅ Todas as dependências instaladas com sucesso!')"
|
| 89 |
+
|
| 90 |
+
# Executar aplicação em modo de teste
|
| 91 |
+
python app.py
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
## Resolução de Problemas Comuns
|
| 95 |
+
|
| 96 |
+
### Erro de Compilação do Rust (tokenizers)
|
| 97 |
+
|
| 98 |
+
**Problema**: Erro ao instalar `tokenizers` devido à falta do Rust
|
| 99 |
+
|
| 100 |
+
**Solução**:
|
| 101 |
+
```bash
|
| 102 |
+
# Instalar versão mais recente do transformers
|
| 103 |
+
pip install --upgrade transformers
|
| 104 |
+
|
| 105 |
+
# Ou instalar Rust (alternativa)
|
| 106 |
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
| 107 |
+
```
|
| 108 |
+
|
| 109 |
+
### Erro do pyaudioop (Python 3.13)
|
| 110 |
+
|
| 111 |
+
**Problema**: `ModuleNotFoundError: No module named 'pyaudioop'`
|
| 112 |
+
|
| 113 |
+
**Solução**:
|
| 114 |
+
```bash
|
| 115 |
+
# Atualizar Gradio para versão mais recente
|
| 116 |
+
pip install --upgrade gradio
|
| 117 |
+
|
| 118 |
+
# O audioop-lts será instalado automaticamente
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
### Problemas de Memória
|
| 122 |
+
|
| 123 |
+
**Problema**: Erro de memória ao carregar modelos de IA
|
| 124 |
+
|
| 125 |
+
**Solução**:
|
| 126 |
+
- Usar modelos menores (configurar em `config.py`)
|
| 127 |
+
- Aumentar memória virtual do sistema
|
| 128 |
+
- Executar em modo standalone (sem IA)
|
| 129 |
+
|
| 130 |
+
### Problemas de Permissão (Windows)
|
| 131 |
+
|
| 132 |
+
**Problema**: Erro de permissão ao executar scripts
|
| 133 |
+
|
| 134 |
+
**Solução**:
|
| 135 |
+
```powershell
|
| 136 |
+
# Alterar política de execução
|
| 137 |
+
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
## Configuração Inicial
|
| 141 |
+
|
| 142 |
+
### 1. Configurar Modelos de IA
|
| 143 |
+
|
| 144 |
+
Edite o arquivo `config.py`:
|
| 145 |
+
|
| 146 |
+
```python
|
| 147 |
+
# Modelos disponíveis (do menor para o maior)
|
| 148 |
+
FINANCIAL_MODELS = [
|
| 149 |
+
"nlptown/bert-base-multilingual-uncased-sentiment", # Mais leve
|
| 150 |
+
"cardiffnlp/twitter-roberta-base-sentiment-latest", # Médio
|
| 151 |
+
"ProsusAI/finbert" # Mais pesado, melhor precisão
|
| 152 |
+
]
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
### 2. Configurar Caminhos de Log
|
| 156 |
+
|
| 157 |
+
Para integração em tempo real, configure o caminho dos logs:
|
| 158 |
+
|
| 159 |
+
```python
|
| 160 |
+
# Em real_time_integration.py
|
| 161 |
+
LOG_FILE_PATH = "caminho/para/seus/logs/trading.log"
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
### 3. Configurar Interface
|
| 165 |
+
|
| 166 |
+
Personalize a interface em `config.py`:
|
| 167 |
+
|
| 168 |
+
```python
|
| 169 |
+
class UIConfig:
|
| 170 |
+
TITLE = "Seu Título Personalizado"
|
| 171 |
+
SUBTITLE = "Sua Descrição"
|
| 172 |
+
# ... outras configurações
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
## Execução
|
| 176 |
+
|
| 177 |
+
### Modo Desenvolvimento
|
| 178 |
+
```bash
|
| 179 |
+
# Executar com reload automático
|
| 180 |
+
python app.py
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
### Modo Produção
|
| 184 |
+
```bash
|
| 185 |
+
# Executar com configurações otimizadas
|
| 186 |
+
python app.py --share=False --server_name=0.0.0.0 --server_port=7860
|
| 187 |
+
```
|
| 188 |
+
|
| 189 |
+
### Modo Standalone (Sem IA)
|
| 190 |
+
```bash
|
| 191 |
+
# Executar sem dependências de IA
|
| 192 |
+
python app.py --no-ai
|
| 193 |
+
```
|
| 194 |
+
|
| 195 |
+
## Verificação Final
|
| 196 |
+
|
| 197 |
+
Após a instalação, verifique se:
|
| 198 |
+
|
| 199 |
+
1. ✅ O aplicativo inicia sem erros
|
| 200 |
+
2. ✅ A interface web carrega em `http://localhost:7860`
|
| 201 |
+
3. ✅ As análises técnicas funcionam
|
| 202 |
+
4. ✅ Os modelos de IA carregam (se disponíveis)
|
| 203 |
+
5. ✅ O monitoramento de performance está ativo
|
| 204 |
+
|
| 205 |
+
## Próximos Passos
|
| 206 |
+
|
| 207 |
+
- Leia a [Documentação da Arquitetura](architecture.md)
|
| 208 |
+
- Configure suas preferências em [Configuração](configuration.md)
|
| 209 |
+
- Explore a [Referência da API](api-reference.md)
|
| 210 |
+
|
| 211 |
+
## Suporte
|
| 212 |
+
|
| 213 |
+
Para problemas não cobertos neste guia, consulte:
|
| 214 |
+
- [Troubleshooting](troubleshooting.md)
|
| 215 |
+
- [Issues do GitHub](link-para-issues)
|
| 216 |
+
- Documentação oficial das dependências
|
docs/troubleshooting.md
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🔧 Guia de Troubleshooting
|
| 2 |
+
|
| 3 |
+
## Visão Geral
|
| 4 |
+
|
| 5 |
+
Este guia ajuda a resolver problemas comuns encontrados ao usar o Vampire Trading Bot. Os problemas estão organizados por categoria com soluções passo a passo.
|
| 6 |
+
|
| 7 |
+
## 🚨 Problemas de Instalação
|
| 8 |
+
|
| 9 |
+
### Erro: "Microsoft Visual C++ 14.0 is required"
|
| 10 |
+
|
| 11 |
+
**Sintomas**:
|
| 12 |
+
```
|
| 13 |
+
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools"
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
**Causa**: Falta de ferramentas de compilação no Windows.
|
| 17 |
+
|
| 18 |
+
**Solução**:
|
| 19 |
+
1. Baixe e instale o [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
|
| 20 |
+
2. Durante a instalação, selecione "C++ build tools"
|
| 21 |
+
3. Reinicie o terminal e tente novamente:
|
| 22 |
+
```bash
|
| 23 |
+
pip install -r requirements.txt
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
### Erro: "No module named 'pyaudioop'"
|
| 27 |
+
|
| 28 |
+
**Sintomas**:
|
| 29 |
+
```
|
| 30 |
+
ModuleNotFoundError: No module named 'pyaudioop'
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
**Causa**: Incompatibilidade com Python 3.13.
|
| 34 |
+
|
| 35 |
+
**Solução**:
|
| 36 |
+
```bash
|
| 37 |
+
# Atualizar Gradio para versão mais recente
|
| 38 |
+
pip install --upgrade gradio
|
| 39 |
+
|
| 40 |
+
# Verificar se audioop-lts foi instalado
|
| 41 |
+
pip list | grep audioop
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
### Erro: "Failed building wheel for tokenizers"
|
| 45 |
+
|
| 46 |
+
**Sintomas**:
|
| 47 |
+
```
|
| 48 |
+
Failed building wheel for tokenizers
|
| 49 |
+
ERROR: Could not build wheels for tokenizers
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
**Causa**: Falta do compilador Rust.
|
| 53 |
+
|
| 54 |
+
**Soluções**:
|
| 55 |
+
|
| 56 |
+
**Opção 1 - Usar versão pré-compilada**:
|
| 57 |
+
```bash
|
| 58 |
+
pip install --upgrade transformers
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
**Opção 2 - Instalar Rust**:
|
| 62 |
+
```bash
|
| 63 |
+
# Windows (PowerShell)
|
| 64 |
+
Invoke-WebRequest -Uri https://win.rustup.rs/ -OutFile rustup-init.exe
|
| 65 |
+
.\rustup-init.exe
|
| 66 |
+
|
| 67 |
+
# Linux/macOS
|
| 68 |
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
| 69 |
+
source ~/.cargo/env
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
### Erro: "Permission denied" (Windows)
|
| 73 |
+
|
| 74 |
+
**Sintomas**:
|
| 75 |
+
```
|
| 76 |
+
PermissionError: [WinError 5] Access is denied
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
**Solução**:
|
| 80 |
+
```powershell
|
| 81 |
+
# Alterar política de execução
|
| 82 |
+
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
| 83 |
+
|
| 84 |
+
# Executar como administrador se necessário
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
## 🤖 Problemas com Modelos de IA
|
| 88 |
+
|
| 89 |
+
### Erro: "OutOfMemoryError" ao carregar modelo
|
| 90 |
+
|
| 91 |
+
**Sintomas**:
|
| 92 |
+
```
|
| 93 |
+
torch.cuda.OutOfMemoryError: CUDA out of memory
|
| 94 |
+
# ou
|
| 95 |
+
RuntimeError: [enforce fail at alloc_cpu.cpp:75]
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
**Soluções**:
|
| 99 |
+
|
| 100 |
+
**1. Usar modelo mais leve**:
|
| 101 |
+
```python
|
| 102 |
+
# Em config.py
|
| 103 |
+
FinancialModels.DEFAULT_MODEL = "nlptown/bert-base-multilingual-uncased-sentiment"
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
**2. Reduzir tamanho do texto**:
|
| 107 |
+
```python
|
| 108 |
+
# Em config.py
|
| 109 |
+
AIConfig.MAX_TEXT_LENGTH = 256 # Reduzir de 512
|
| 110 |
+
AIConfig.BATCH_SIZE = 4 # Reduzir de 8
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
**3. Forçar uso de CPU**:
|
| 114 |
+
```python
|
| 115 |
+
# Em config.py
|
| 116 |
+
AIConfig.USE_GPU = False
|
| 117 |
+
```
|
| 118 |
+
|
| 119 |
+
**4. Executar em modo standalone**:
|
| 120 |
+
```bash
|
| 121 |
+
python app.py --no-ai
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
### Erro: "Model not found" ou "Repository not found"
|
| 125 |
+
|
| 126 |
+
**Sintomas**:
|
| 127 |
+
```
|
| 128 |
+
OSError: Repository not found
|
| 129 |
+
HTTPError: 404 Client Error
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
**Soluções**:
|
| 133 |
+
|
| 134 |
+
**1. Verificar conectividade**:
|
| 135 |
+
```python
|
| 136 |
+
import requests
|
| 137 |
+
response = requests.get("https://huggingface.co")
|
| 138 |
+
print(f"Status: {response.status_code}")
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
**2. Usar modelo alternativo**:
|
| 142 |
+
```python
|
| 143 |
+
# Em config.py
|
| 144 |
+
FinancialModels.DEFAULT_MODEL = "cardiffnlp/twitter-roberta-base-sentiment-latest"
|
| 145 |
+
```
|
| 146 |
+
|
| 147 |
+
**3. Cache offline**:
|
| 148 |
+
```bash
|
| 149 |
+
# Baixar modelo manualmente
|
| 150 |
+
from transformers import pipeline
|
| 151 |
+
pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
### Modelo carrega muito lentamente
|
| 155 |
+
|
| 156 |
+
**Sintomas**: Demora excessiva no primeiro carregamento.
|
| 157 |
+
|
| 158 |
+
**Soluções**:
|
| 159 |
+
|
| 160 |
+
**1. Habilitar cache**:
|
| 161 |
+
```python
|
| 162 |
+
# Em config.py
|
| 163 |
+
AIConfig.ENABLE_MODEL_CACHE = True
|
| 164 |
+
AIConfig.CACHE_SIZE = 100
|
| 165 |
+
```
|
| 166 |
+
|
| 167 |
+
**2. Pré-carregar modelos**:
|
| 168 |
+
```python
|
| 169 |
+
# Adicionar ao início do app.py
|
| 170 |
+
from sentiment_analysis import SentimentAnalysisEngine
|
| 171 |
+
engine = SentimentAnalysisEngine()
|
| 172 |
+
engine.model_manager.load_model() # Pré-carrega
|
| 173 |
+
```
|
| 174 |
+
|
| 175 |
+
**3. Usar modelo local**:
|
| 176 |
+
```bash
|
| 177 |
+
# Baixar modelo para pasta local
|
| 178 |
+
mkdir models
|
| 179 |
+
# Configurar caminho local em config.py
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
## 🖥️ Problemas de Interface
|
| 183 |
+
|
| 184 |
+
### Interface não carrega (erro 500)
|
| 185 |
+
|
| 186 |
+
**Sintomas**: Página em branco ou erro 500 no navegador.
|
| 187 |
+
|
| 188 |
+
**Diagnóstico**:
|
| 189 |
+
```bash
|
| 190 |
+
# Verificar logs no terminal
|
| 191 |
+
python -u app.py
|
| 192 |
+
|
| 193 |
+
# Verificar porta
|
| 194 |
+
netstat -an | findstr 7860
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
**Soluções**:
|
| 198 |
+
|
| 199 |
+
**1. Verificar dependências**:
|
| 200 |
+
```python
|
| 201 |
+
try:
|
| 202 |
+
import gradio as gr
|
| 203 |
+
print(f"Gradio versão: {gr.__version__}")
|
| 204 |
+
except ImportError as e:
|
| 205 |
+
print(f"Erro ao importar Gradio: {e}")
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
**2. Usar porta diferente**:
|
| 209 |
+
```bash
|
| 210 |
+
python app.py --server_port=8080
|
| 211 |
+
```
|
| 212 |
+
|
| 213 |
+
**3. Modo debug**:
|
| 214 |
+
```python
|
| 215 |
+
# Em app.py
|
| 216 |
+
demo.launch(debug=True, show_error=True)
|
| 217 |
+
```
|
| 218 |
+
|
| 219 |
+
### Interface trava durante análise
|
| 220 |
+
|
| 221 |
+
**Sintomas**: Interface não responde após submeter análise.
|
| 222 |
+
|
| 223 |
+
**Soluções**:
|
| 224 |
+
|
| 225 |
+
**1. Verificar timeout**:
|
| 226 |
+
```python
|
| 227 |
+
# Em config.py
|
| 228 |
+
AIConfig.MODEL_LOAD_TIMEOUT = 120 # Aumentar timeout
|
| 229 |
+
```
|
| 230 |
+
|
| 231 |
+
**2. Adicionar indicador de progresso**:
|
| 232 |
+
```python
|
| 233 |
+
# Em ui.py
|
| 234 |
+
def analyze_with_progress(text):
|
| 235 |
+
yield "🔄 Iniciando análise..."
|
| 236 |
+
# ... análise
|
| 237 |
+
yield "✅ Análise concluída!"
|
| 238 |
+
```
|
| 239 |
+
|
| 240 |
+
**3. Processamento assíncrono**:
|
| 241 |
+
```python
|
| 242 |
+
import asyncio
|
| 243 |
+
|
| 244 |
+
async def async_analysis(text):
|
| 245 |
+
# Análise em background
|
| 246 |
+
pass
|
| 247 |
+
```
|
| 248 |
+
|
| 249 |
+
### Erro: "Connection refused" ou "Address already in use"
|
| 250 |
+
|
| 251 |
+
**Sintomas**:
|
| 252 |
+
```
|
| 253 |
+
OSError: [Errno 98] Address already in use
|
| 254 |
+
ConnectionRefusedError: [Errno 111] Connection refused
|
| 255 |
+
```
|
| 256 |
+
|
| 257 |
+
**Soluções**:
|
| 258 |
+
|
| 259 |
+
**1. Verificar processos na porta**:
|
| 260 |
+
```bash
|
| 261 |
+
# Windows
|
| 262 |
+
netstat -ano | findstr :7860
|
| 263 |
+
taskkill /PID <PID> /F
|
| 264 |
+
|
| 265 |
+
# Linux/macOS
|
| 266 |
+
lsof -ti:7860 | xargs kill -9
|
| 267 |
+
```
|
| 268 |
+
|
| 269 |
+
**2. Usar porta diferente**:
|
| 270 |
+
```bash
|
| 271 |
+
python app.py --server_port=8080
|
| 272 |
+
```
|
| 273 |
+
|
| 274 |
+
## 📊 Problemas de Performance
|
| 275 |
+
|
| 276 |
+
### Alto uso de CPU/Memória
|
| 277 |
+
|
| 278 |
+
**Sintomas**: Sistema lento, ventilador alto.
|
| 279 |
+
|
| 280 |
+
**Diagnóstico**:
|
| 281 |
+
```python
|
| 282 |
+
# Verificar uso de recursos
|
| 283 |
+
from performance_monitor import PerformanceMonitor
|
| 284 |
+
monitor = PerformanceMonitor()
|
| 285 |
+
monitor.start_monitoring()
|
| 286 |
+
print(monitor.get_current_metrics())
|
| 287 |
+
```
|
| 288 |
+
|
| 289 |
+
**Soluções**:
|
| 290 |
+
|
| 291 |
+
**1. Otimizar configurações**:
|
| 292 |
+
```python
|
| 293 |
+
# Em config.py
|
| 294 |
+
AIConfig.MAX_TEXT_LENGTH = 256
|
| 295 |
+
AIConfig.BATCH_SIZE = 2
|
| 296 |
+
AIConfig.USE_GPU = False
|
| 297 |
+
```
|
| 298 |
+
|
| 299 |
+
**2. Limitar histórico**:
|
| 300 |
+
```python
|
| 301 |
+
# Em performance_monitor.py
|
| 302 |
+
PerformanceMonitor(max_metrics_history=100)
|
| 303 |
+
```
|
| 304 |
+
|
| 305 |
+
**3. Desabilitar recursos**:
|
| 306 |
+
```python
|
| 307 |
+
# Desabilitar monitoramento em tempo real
|
| 308 |
+
real_time_integration = None
|
| 309 |
+
```
|
| 310 |
+
|
| 311 |
+
### Análises muito lentas
|
| 312 |
+
|
| 313 |
+
**Sintomas**: Demora excessiva para gerar resultados.
|
| 314 |
+
|
| 315 |
+
**Soluções**:
|
| 316 |
+
|
| 317 |
+
**1. Profile de performance**:
|
| 318 |
+
```python
|
| 319 |
+
import cProfile
|
| 320 |
+
|
| 321 |
+
def profile_analysis():
|
| 322 |
+
cProfile.run('engine.analyze_market_data(text)')
|
| 323 |
+
```
|
| 324 |
+
|
| 325 |
+
**2. Cache de resultados**:
|
| 326 |
+
```python
|
| 327 |
+
from functools import lru_cache
|
| 328 |
+
|
| 329 |
+
@lru_cache(maxsize=100)
|
| 330 |
+
def cached_analysis(text_hash):
|
| 331 |
+
return engine.analyze_market_data(text)
|
| 332 |
+
```
|
| 333 |
+
|
| 334 |
+
**3. Processamento paralelo**:
|
| 335 |
+
```python
|
| 336 |
+
from concurrent.futures import ThreadPoolExecutor
|
| 337 |
+
|
| 338 |
+
with ThreadPoolExecutor(max_workers=2) as executor:
|
| 339 |
+
future = executor.submit(analyze_function, text)
|
| 340 |
+
result = future.result(timeout=30)
|
| 341 |
+
```
|
| 342 |
+
|
| 343 |
+
## 📁 Problemas de Arquivos e Logs
|
| 344 |
+
|
| 345 |
+
### Erro: "File not found" para logs
|
| 346 |
+
|
| 347 |
+
**Sintomas**:
|
| 348 |
+
```
|
| 349 |
+
FileNotFoundError: [Errno 2] No such file or directory: 'trading.log'
|
| 350 |
+
```
|
| 351 |
+
|
| 352 |
+
**Soluções**:
|
| 353 |
+
|
| 354 |
+
**1. Verificar caminho**:
|
| 355 |
+
```python
|
| 356 |
+
from pathlib import Path
|
| 357 |
+
log_path = Path("d:/hugging_face_spaces/text")
|
| 358 |
+
print(f"Existe: {log_path.exists()}")
|
| 359 |
+
print(f"É arquivo: {log_path.is_file()}")
|
| 360 |
+
```
|
| 361 |
+
|
| 362 |
+
**2. Criar arquivo de exemplo**:
|
| 363 |
+
```python
|
| 364 |
+
# Criar log de exemplo para testes
|
| 365 |
+
with open("sample_log.txt", "w") as f:
|
| 366 |
+
f.write("""⏰ Análise #1 - 09:46:58
|
| 367 |
+
📊 DADOS DE MERCADO - WINV25
|
| 368 |
+
Preço Atual: 140135.00000 ↗
|
| 369 |
+
""")
|
| 370 |
+
```
|
| 371 |
+
|
| 372 |
+
**3. Configurar caminho correto**:
|
| 373 |
+
```python
|
| 374 |
+
# Em real_time_integration.py
|
| 375 |
+
LOG_FILE_PATH = "caminho/correto/para/logs.txt"
|
| 376 |
+
```
|
| 377 |
+
|
| 378 |
+
### Erro de permissão em arquivos
|
| 379 |
+
|
| 380 |
+
**Sintomas**:
|
| 381 |
+
```
|
| 382 |
+
PermissionError: [Errno 13] Permission denied
|
| 383 |
+
```
|
| 384 |
+
|
| 385 |
+
**Soluções**:
|
| 386 |
+
|
| 387 |
+
**1. Verificar permissões**:
|
| 388 |
+
```bash
|
| 389 |
+
# Linux/macOS
|
| 390 |
+
ls -la arquivo.log
|
| 391 |
+
chmod 644 arquivo.log
|
| 392 |
+
|
| 393 |
+
# Windows
|
| 394 |
+
icacls arquivo.log
|
| 395 |
+
```
|
| 396 |
+
|
| 397 |
+
**2. Executar como administrador**:
|
| 398 |
+
```bash
|
| 399 |
+
# Windows (PowerShell como Admin)
|
| 400 |
+
python app.py
|
| 401 |
+
```
|
| 402 |
+
|
| 403 |
+
## 🔄 Problemas de Integração em Tempo Real
|
| 404 |
+
|
| 405 |
+
### FileWatcher não detecta mudanças
|
| 406 |
+
|
| 407 |
+
**Sintomas**: Logs atualizados mas sistema não processa.
|
| 408 |
+
|
| 409 |
+
**Diagnóstico**:
|
| 410 |
+
```python
|
| 411 |
+
from real_time_integration import FileWatcher
|
| 412 |
+
|
| 413 |
+
def test_callback(content):
|
| 414 |
+
print(f"Arquivo mudou: {len(content)} caracteres")
|
| 415 |
+
|
| 416 |
+
watcher = FileWatcher("test.txt", test_callback)
|
| 417 |
+
watcher.start()
|
| 418 |
+
```
|
| 419 |
+
|
| 420 |
+
**Soluções**:
|
| 421 |
+
|
| 422 |
+
**1. Verificar intervalo**:
|
| 423 |
+
```python
|
| 424 |
+
# Em real_time_integration.py
|
| 425 |
+
config = RealTimeConfig(
|
| 426 |
+
log_file_path="logs.txt",
|
| 427 |
+
check_interval=0.5 # Reduzir intervalo
|
| 428 |
+
)
|
| 429 |
+
```
|
| 430 |
+
|
| 431 |
+
**2. Forçar flush do arquivo**:
|
| 432 |
+
```python
|
| 433 |
+
# No sistema que gera logs
|
| 434 |
+
with open("logs.txt", "a") as f:
|
| 435 |
+
f.write("nova linha\n")
|
| 436 |
+
f.flush() # Forçar escrita
|
| 437 |
+
```
|
| 438 |
+
|
| 439 |
+
### Eventos duplicados
|
| 440 |
+
|
| 441 |
+
**Sintomas**: Mesmo evento processado múltiplas vezes.
|
| 442 |
+
|
| 443 |
+
**Solução**:
|
| 444 |
+
```python
|
| 445 |
+
# Adicionar deduplicação
|
| 446 |
+
class EventDeduplicator:
|
| 447 |
+
def __init__(self):
|
| 448 |
+
self.processed_events = set()
|
| 449 |
+
|
| 450 |
+
def is_duplicate(self, event_hash):
|
| 451 |
+
if event_hash in self.processed_events:
|
| 452 |
+
return True
|
| 453 |
+
self.processed_events.add(event_hash)
|
| 454 |
+
return False
|
| 455 |
+
```
|
| 456 |
+
|
| 457 |
+
## 🧪 Problemas de Desenvolvimento
|
| 458 |
+
|
| 459 |
+
### Erro ao importar módulos
|
| 460 |
+
|
| 461 |
+
**Sintomas**:
|
| 462 |
+
```
|
| 463 |
+
ModuleNotFoundError: No module named 'market_analysis'
|
| 464 |
+
```
|
| 465 |
+
|
| 466 |
+
**Soluções**:
|
| 467 |
+
|
| 468 |
+
**1. Verificar PYTHONPATH**:
|
| 469 |
+
```bash
|
| 470 |
+
# Windows
|
| 471 |
+
set PYTHONPATH=%PYTHONPATH%;D:\hugging_face_spaces
|
| 472 |
+
|
| 473 |
+
# Linux/macOS
|
| 474 |
+
export PYTHONPATH=$PYTHONPATH:/path/to/project
|
| 475 |
+
```
|
| 476 |
+
|
| 477 |
+
**2. Usar imports relativos**:
|
| 478 |
+
```python
|
| 479 |
+
# Em vez de
|
| 480 |
+
from market_analysis import TechnicalAnalysisEngine
|
| 481 |
+
|
| 482 |
+
# Use
|
| 483 |
+
from .market_analysis import TechnicalAnalysisEngine
|
| 484 |
+
```
|
| 485 |
+
|
| 486 |
+
**3. Adicionar ao sys.path**:
|
| 487 |
+
```python
|
| 488 |
+
import sys
|
| 489 |
+
import os
|
| 490 |
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
| 491 |
+
```
|
| 492 |
+
|
| 493 |
+
### Erro de encoding
|
| 494 |
+
|
| 495 |
+
**Sintomas**:
|
| 496 |
+
```
|
| 497 |
+
UnicodeDecodeError: 'utf-8' codec can't decode
|
| 498 |
+
```
|
| 499 |
+
|
| 500 |
+
**Soluções**:
|
| 501 |
+
|
| 502 |
+
**1. Especificar encoding**:
|
| 503 |
+
```python
|
| 504 |
+
with open("arquivo.txt", "r", encoding="utf-8") as f:
|
| 505 |
+
content = f.read()
|
| 506 |
+
```
|
| 507 |
+
|
| 508 |
+
**2. Detectar encoding automaticamente**:
|
| 509 |
+
```python
|
| 510 |
+
import chardet
|
| 511 |
+
|
| 512 |
+
with open("arquivo.txt", "rb") as f:
|
| 513 |
+
raw_data = f.read()
|
| 514 |
+
encoding = chardet.detect(raw_data)['encoding']
|
| 515 |
+
content = raw_data.decode(encoding)
|
| 516 |
+
```
|
| 517 |
+
|
| 518 |
+
## 🔍 Ferramentas de Diagnóstico
|
| 519 |
+
|
| 520 |
+
### Script de Diagnóstico Completo
|
| 521 |
+
|
| 522 |
+
```python
|
| 523 |
+
#!/usr/bin/env python3
|
| 524 |
+
# diagnostic.py
|
| 525 |
+
|
| 526 |
+
import sys
|
| 527 |
+
import os
|
| 528 |
+
import importlib
|
| 529 |
+
import platform
|
| 530 |
+
import psutil
|
| 531 |
+
from pathlib import Path
|
| 532 |
+
|
| 533 |
+
def run_diagnostics():
|
| 534 |
+
print("🔍 DIAGNÓSTICO DO VAMPIRE TRADING BOT")
|
| 535 |
+
print("=" * 50)
|
| 536 |
+
|
| 537 |
+
# Sistema
|
| 538 |
+
print(f"🖥️ Sistema: {platform.system()} {platform.release()}")
|
| 539 |
+
print(f"🐍 Python: {sys.version}")
|
| 540 |
+
print(f"📁 Diretório: {os.getcwd()}")
|
| 541 |
+
|
| 542 |
+
# Recursos
|
| 543 |
+
print(f"💾 RAM: {psutil.virtual_memory().total // (1024**3)} GB")
|
| 544 |
+
print(f"🔥 CPU: {psutil.cpu_count()} cores")
|
| 545 |
+
|
| 546 |
+
# Dependências
|
| 547 |
+
print("\n📦 DEPENDÊNCIAS:")
|
| 548 |
+
dependencies = [
|
| 549 |
+
'gradio', 'transformers', 'torch',
|
| 550 |
+
'numpy', 'pandas', 'scipy', 'psutil'
|
| 551 |
+
]
|
| 552 |
+
|
| 553 |
+
for dep in dependencies:
|
| 554 |
+
try:
|
| 555 |
+
module = importlib.import_module(dep)
|
| 556 |
+
version = getattr(module, '__version__', 'N/A')
|
| 557 |
+
print(f"✅ {dep}: {version}")
|
| 558 |
+
except ImportError:
|
| 559 |
+
print(f"❌ {dep}: NÃO INSTALADO")
|
| 560 |
+
|
| 561 |
+
# Arquivos
|
| 562 |
+
print("\n📁 ARQUIVOS:")
|
| 563 |
+
files = [
|
| 564 |
+
'app.py', 'config.py', 'market_analysis.py',
|
| 565 |
+
'sentiment_analysis.py', 'ui.py', 'requirements.txt'
|
| 566 |
+
]
|
| 567 |
+
|
| 568 |
+
for file in files:
|
| 569 |
+
path = Path(file)
|
| 570 |
+
if path.exists():
|
| 571 |
+
size = path.stat().st_size
|
| 572 |
+
print(f"✅ {file}: {size} bytes")
|
| 573 |
+
else:
|
| 574 |
+
print(f"❌ {file}: NÃO ENCONTRADO")
|
| 575 |
+
|
| 576 |
+
# Teste de importação
|
| 577 |
+
print("\n🧪 TESTE DE IMPORTAÇÃO:")
|
| 578 |
+
modules = [
|
| 579 |
+
'market_analysis', 'sentiment_analysis',
|
| 580 |
+
'fibonacci_analysis', 'ui'
|
| 581 |
+
]
|
| 582 |
+
|
| 583 |
+
for module in modules:
|
| 584 |
+
try:
|
| 585 |
+
importlib.import_module(module)
|
| 586 |
+
print(f"✅ {module}: OK")
|
| 587 |
+
except Exception as e:
|
| 588 |
+
print(f"❌ {module}: {str(e)[:50]}...")
|
| 589 |
+
|
| 590 |
+
print("\n🏁 Diagnóstico concluído!")
|
| 591 |
+
|
| 592 |
+
if __name__ == "__main__":
|
| 593 |
+
run_diagnostics()
|
| 594 |
+
```
|
| 595 |
+
|
| 596 |
+
### Script de Teste de Performance
|
| 597 |
+
|
| 598 |
+
```python
|
| 599 |
+
#!/usr/bin/env python3
|
| 600 |
+
# performance_test.py
|
| 601 |
+
|
| 602 |
+
import time
|
| 603 |
+
import psutil
|
| 604 |
+
from memory_profiler import profile
|
| 605 |
+
|
| 606 |
+
@profile
|
| 607 |
+
def test_analysis_performance():
|
| 608 |
+
"""Testa performance das análises."""
|
| 609 |
+
|
| 610 |
+
# Simular análise técnica
|
| 611 |
+
start_time = time.time()
|
| 612 |
+
|
| 613 |
+
# Teste de análise
|
| 614 |
+
sample_text = "Preço: 140135, Variação: +5, Volume: 5023"
|
| 615 |
+
|
| 616 |
+
try:
|
| 617 |
+
from market_analysis import TechnicalAnalysisEngine
|
| 618 |
+
engine = TechnicalAnalysisEngine()
|
| 619 |
+
result = engine.analyze_market_data(sample_text)
|
| 620 |
+
|
| 621 |
+
end_time = time.time()
|
| 622 |
+
analysis_time = end_time - start_time
|
| 623 |
+
|
| 624 |
+
print(f"⏱️ Tempo de análise: {analysis_time:.2f}s")
|
| 625 |
+
print(f"💾 Uso de memória: {psutil.Process().memory_info().rss / 1024 / 1024:.1f} MB")
|
| 626 |
+
print(f"🔥 Uso de CPU: {psutil.cpu_percent()}%")
|
| 627 |
+
|
| 628 |
+
return True
|
| 629 |
+
|
| 630 |
+
except Exception as e:
|
| 631 |
+
print(f"❌ Erro na análise: {e}")
|
| 632 |
+
return False
|
| 633 |
+
|
| 634 |
+
if __name__ == "__main__":
|
| 635 |
+
test_analysis_performance()
|
| 636 |
+
```
|
| 637 |
+
|
| 638 |
+
## 📞 Obtendo Ajuda
|
| 639 |
+
|
| 640 |
+
### Informações para Suporte
|
| 641 |
+
|
| 642 |
+
Ao reportar problemas, inclua:
|
| 643 |
+
|
| 644 |
+
1. **Informações do sistema**:
|
| 645 |
+
```bash
|
| 646 |
+
python diagnostic.py > diagnostic_report.txt
|
| 647 |
+
```
|
| 648 |
+
|
| 649 |
+
2. **Logs de erro completos**:
|
| 650 |
+
```bash
|
| 651 |
+
python app.py > app_log.txt 2>&1
|
| 652 |
+
```
|
| 653 |
+
|
| 654 |
+
3. **Configurações utilizadas**:
|
| 655 |
+
```python
|
| 656 |
+
# Exportar configurações
|
| 657 |
+
from config import *
|
| 658 |
+
print(f"Modelo: {FinancialModels.DEFAULT_MODEL}")
|
| 659 |
+
print(f"RSI: {TechnicalAnalysis.RSI_PERIOD}")
|
| 660 |
+
```
|
| 661 |
+
|
| 662 |
+
### Recursos Adicionais
|
| 663 |
+
|
| 664 |
+
- 📚 [Documentação da Arquitetura](architecture.md)
|
| 665 |
+
- ⚙️ [Guia de Configuração](configuration.md)
|
| 666 |
+
- 🚀 [Guia de Instalação](installation.md)
|
| 667 |
+
- 📖 [Referência da API](api-reference.md)
|
| 668 |
+
- 👨💻 [Guia do Desenvolvedor](developer-guide.md)
|
| 669 |
+
|
| 670 |
+
### Comunidade e Suporte
|
| 671 |
+
|
| 672 |
+
- 🐛 **Issues**: Para reportar bugs
|
| 673 |
+
- 💬 **Discussões**: Para dúvidas gerais
|
| 674 |
+
- 📧 **Email**: Para suporte direto
|
| 675 |
+
- 📖 **Wiki**: Para documentação adicional
|
| 676 |
+
|
| 677 |
+
---
|
| 678 |
+
|
| 679 |
+
**💡 Dica**: Mantenha sempre uma cópia de backup das suas configurações antes de fazer alterações significativas!
|
fibonacci_analysis.py
ADDED
|
@@ -0,0 +1,528 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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()
|
src/__init__.py
ADDED
|
File without changes
|
src/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (135 Bytes). View file
|
|
|
src/analysis/__init__.py
ADDED
|
File without changes
|
src/analysis/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (144 Bytes). View file
|
|
|
src/analysis/__pycache__/market_analysis.cpython-313.pyc
ADDED
|
Binary file (23.9 kB). View file
|
|
|
src/analysis/fibonacci_analysis.py
ADDED
|
@@ -0,0 +1,528 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 src.core.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))
|
market_analysis.py → src/analysis/market_analysis.py
RENAMED
|
@@ -4,13 +4,23 @@ import re
|
|
| 4 |
from typing import Dict, List, Optional, Any
|
| 5 |
from dataclasses import dataclass
|
| 6 |
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
from config import (
|
| 9 |
TechnicalAnalysisConfig,
|
| 10 |
ScoringConfig,
|
| 11 |
TradingConfig,
|
| 12 |
RegexPatterns
|
| 13 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
|
| 16 |
@dataclass
|
|
@@ -332,6 +342,11 @@ class TechnicalAnalysisEngine:
|
|
| 332 |
self.momentum_analyzer = MomentumAnalyzer()
|
| 333 |
self.volume_analyzer = VolumeAnalyzer()
|
| 334 |
self.setup_detector = ScalpingSetupDetector()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
def analyze(self, market_data: MarketData) -> Dict[str, Any]:
|
| 337 |
"""Executa análise técnica completa."""
|
|
@@ -348,14 +363,22 @@ class TechnicalAnalysisEngine:
|
|
| 348 |
special_signals = self.setup_detector.detect_perfect_setups(market_data, signals)
|
| 349 |
all_signals = signals + special_signals
|
| 350 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
# Calcular ação e confiança
|
| 352 |
action, confidence = self._calculate_action_and_confidence(all_signals)
|
| 353 |
|
| 354 |
return {
|
| 355 |
'action': action,
|
| 356 |
'confidence': confidence,
|
| 357 |
-
|
| 358 |
-
|
|
|
|
|
|
|
| 359 |
}
|
| 360 |
|
| 361 |
def _calculate_action_and_confidence(self, signals: List[TechnicalSignal]) -> tuple[str, int]:
|
|
@@ -390,6 +413,124 @@ class TechnicalAnalysisEngine:
|
|
| 390 |
min(ScoringConfig.MAX_CONFIDENCE, confidence_score))
|
| 391 |
|
| 392 |
return action, confidence_score
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
|
| 394 |
|
| 395 |
class RiskCalculator:
|
|
|
|
| 4 |
from typing import Dict, List, Optional, Any
|
| 5 |
from dataclasses import dataclass
|
| 6 |
from datetime import datetime
|
| 7 |
+
import numpy as np
|
| 8 |
+
import pandas as pd
|
| 9 |
+
import logging
|
| 10 |
|
| 11 |
+
from config.config import (
|
| 12 |
TechnicalAnalysisConfig,
|
| 13 |
ScoringConfig,
|
| 14 |
TradingConfig,
|
| 15 |
RegexPatterns
|
| 16 |
)
|
| 17 |
+
from ..utils.utils import calculate_rsi, calculate_bollinger_bands, calculate_ema, format_number
|
| 18 |
+
from .fibonacci_analysis import AdvancedFibonacciEngine, AdvancedFibonacciAnalysis
|
| 19 |
+
from ..core.log_parser import VampireBotLogParser, BotAnalysis
|
| 20 |
+
|
| 21 |
+
# Configurar logging
|
| 22 |
+
logging.basicConfig(level=logging.INFO)
|
| 23 |
+
logger = logging.getLogger(__name__)
|
| 24 |
|
| 25 |
|
| 26 |
@dataclass
|
|
|
|
| 342 |
self.momentum_analyzer = MomentumAnalyzer()
|
| 343 |
self.volume_analyzer = VolumeAnalyzer()
|
| 344 |
self.setup_detector = ScalpingSetupDetector()
|
| 345 |
+
self.fibonacci_engine = AdvancedFibonacciEngine()
|
| 346 |
+
self.log_parser = VampireBotLogParser()
|
| 347 |
+
self.advanced_processor = AdvancedMarketProcessor()
|
| 348 |
+
self.config = TECHNICAL_CONFIG
|
| 349 |
+
logger.info("TechnicalAnalysisEngine inicializado com processamento avançado")
|
| 350 |
|
| 351 |
def analyze(self, market_data: MarketData) -> Dict[str, Any]:
|
| 352 |
"""Executa análise técnica completa."""
|
|
|
|
| 363 |
special_signals = self.setup_detector.detect_perfect_setups(market_data, signals)
|
| 364 |
all_signals = signals + special_signals
|
| 365 |
|
| 366 |
+
# Análise avançada de Fibonacci
|
| 367 |
+
fibonacci_analysis = self._perform_fibonacci_analysis(market_data)
|
| 368 |
+
|
| 369 |
+
# Processamento avançado de mercado
|
| 370 |
+
advanced_analysis = self._perform_advanced_market_analysis(market_data)
|
| 371 |
+
|
| 372 |
# Calcular ação e confiança
|
| 373 |
action, confidence = self._calculate_action_and_confidence(all_signals)
|
| 374 |
|
| 375 |
return {
|
| 376 |
'action': action,
|
| 377 |
'confidence': confidence,
|
| 378 |
+
'signals': all_signals,
|
| 379 |
+
'fibonacci': fibonacci_analysis,
|
| 380 |
+
'advanced_analysis': advanced_analysis,
|
| 381 |
+
'market_data': market_data
|
| 382 |
}
|
| 383 |
|
| 384 |
def _calculate_action_and_confidence(self, signals: List[TechnicalSignal]) -> tuple[str, int]:
|
|
|
|
| 413 |
min(ScoringConfig.MAX_CONFIDENCE, confidence_score))
|
| 414 |
|
| 415 |
return action, confidence_score
|
| 416 |
+
|
| 417 |
+
def _perform_fibonacci_analysis(self, market_data: MarketData) -> Dict[str, Any]:
|
| 418 |
+
"""Executa análise avançada de Fibonacci."""
|
| 419 |
+
try:
|
| 420 |
+
# Simular dados de preço para análise Fibonacci
|
| 421 |
+
prices = np.array([market_data.price * (1 + np.random.normal(0, 0.01)) for _ in range(100)])
|
| 422 |
+
|
| 423 |
+
# Executar análise Fibonacci
|
| 424 |
+
fib_analysis = self.fibonacci_engine.analyze_fibonacci_levels(
|
| 425 |
+
prices=prices,
|
| 426 |
+
current_price=market_data.price
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
return {
|
| 430 |
+
'levels': fib_analysis.levels if fib_analysis else {},
|
| 431 |
+
'signals': fib_analysis.signals if fib_analysis else [],
|
| 432 |
+
'confluence_zones': fib_analysis.confluence_zones if fib_analysis else [],
|
| 433 |
+
'strength': fib_analysis.overall_strength if fib_analysis else 0
|
| 434 |
+
}
|
| 435 |
+
except Exception as e:
|
| 436 |
+
logger.error(f"Erro na análise Fibonacci: {e}")
|
| 437 |
+
return {
|
| 438 |
+
'levels': {},
|
| 439 |
+
'signals': [],
|
| 440 |
+
'confluence_zones': [],
|
| 441 |
+
'strength': 0
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
def process_bot_log_data(self, log_content: str) -> Dict[str, Any]:
|
| 445 |
+
"""Processa dados de log do bot externo."""
|
| 446 |
+
try:
|
| 447 |
+
# Parse do log
|
| 448 |
+
bot_analysis = self.log_parser.parse_log(log_content)
|
| 449 |
+
|
| 450 |
+
if not bot_analysis:
|
| 451 |
+
return {'error': 'Falha ao processar log do bot'}
|
| 452 |
+
|
| 453 |
+
# Converter para MarketData
|
| 454 |
+
market_data = MarketData(
|
| 455 |
+
price=bot_analysis.market_info.price,
|
| 456 |
+
variation=0, # Será calculado se necessário
|
| 457 |
+
rsi=bot_analysis.technical_indicators.rsi if bot_analysis.technical_indicators else 50,
|
| 458 |
+
ema_trend=bot_analysis.technical_indicators.ema if bot_analysis.technical_indicators else 'NEUTRO',
|
| 459 |
+
bb_position=bot_analysis.technical_indicators.bollinger if bot_analysis.technical_indicators else 'DENTRO',
|
| 460 |
+
volume=bot_analysis.market_info.volume
|
| 461 |
+
)
|
| 462 |
+
|
| 463 |
+
# Executar análise completa
|
| 464 |
+
analysis_result = self.analyze(market_data)
|
| 465 |
+
|
| 466 |
+
# Adicionar dados específicos do bot
|
| 467 |
+
analysis_result['bot_data'] = {
|
| 468 |
+
'fibonacci_alerts': bot_analysis.fibonacci_analysis.alerts if bot_analysis.fibonacci_analysis else 0,
|
| 469 |
+
'fibonacci_signal': bot_analysis.fibonacci_analysis.signal if bot_analysis.fibonacci_analysis else 'UNKNOWN',
|
| 470 |
+
'technical_indicators': {
|
| 471 |
+
'rsi': bot_analysis.technical_indicators.rsi if bot_analysis.technical_indicators else None,
|
| 472 |
+
'ema': bot_analysis.technical_indicators.ema if bot_analysis.technical_indicators else None,
|
| 473 |
+
'bollinger': bot_analysis.technical_indicators.bollinger if bot_analysis.technical_indicators else None,
|
| 474 |
+
'atr': bot_analysis.technical_indicators.atr if bot_analysis.technical_indicators else None
|
| 475 |
+
},
|
| 476 |
+
'original_analysis': bot_analysis
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
return analysis_result
|
| 480 |
+
|
| 481 |
+
except Exception as e:
|
| 482 |
+
logger.error(f"Erro ao processar dados do bot: {e}")
|
| 483 |
+
return {'error': f'Erro no processamento: {str(e)}'}
|
| 484 |
+
|
| 485 |
+
def _perform_advanced_market_analysis(self, market_data: MarketData) -> Dict[str, Any]:
|
| 486 |
+
"""Executa análise avançada de mercado com swing points e padrões harmônicos."""
|
| 487 |
+
try:
|
| 488 |
+
# Simular dados históricos de preço para análise
|
| 489 |
+
base_price = market_data.price
|
| 490 |
+
prices = np.array([base_price * (1 + np.random.normal(0, 0.02)) for _ in range(100)])
|
| 491 |
+
volumes = np.array([1000 + np.random.randint(-200, 200) for _ in range(100)])
|
| 492 |
+
|
| 493 |
+
# Níveis de Fibonacci simulados
|
| 494 |
+
fibonacci_levels = {
|
| 495 |
+
'23.6%': base_price * 0.764,
|
| 496 |
+
'38.2%': base_price * 0.618,
|
| 497 |
+
'50.0%': base_price * 0.5,
|
| 498 |
+
'61.8%': base_price * 0.382,
|
| 499 |
+
'78.6%': base_price * 0.214
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
# Níveis de suporte/resistência simulados
|
| 503 |
+
support_resistance = [
|
| 504 |
+
base_price * 0.95,
|
| 505 |
+
base_price * 0.98,
|
| 506 |
+
base_price * 1.02,
|
| 507 |
+
base_price * 1.05
|
| 508 |
+
]
|
| 509 |
+
|
| 510 |
+
# Executar processamento avançado
|
| 511 |
+
advanced_result = self.advanced_processor.process_market_data(
|
| 512 |
+
prices=prices,
|
| 513 |
+
volumes=volumes,
|
| 514 |
+
fibonacci_levels=fibonacci_levels,
|
| 515 |
+
support_resistance_levels=support_resistance
|
| 516 |
+
)
|
| 517 |
+
|
| 518 |
+
return advanced_result
|
| 519 |
+
|
| 520 |
+
except Exception as e:
|
| 521 |
+
logger.error(f"Erro na análise avançada de mercado: {e}")
|
| 522 |
+
return {
|
| 523 |
+
'swing_points': {'count': 0, 'highs': [], 'lows': [], 'avg_strength': 0},
|
| 524 |
+
'confluence_zones': {'count': 0, 'zones': [], 'strongest_zone': None},
|
| 525 |
+
'harmonic_patterns': {'count': 0, 'patterns': [], 'most_reliable': None},
|
| 526 |
+
'market_structure': 'UNKNOWN',
|
| 527 |
+
'key_levels': []
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
class MarketAnalyzer:
|
| 532 |
+
"""Analisador principal de mercado."""
|
| 533 |
+
pass
|
| 534 |
|
| 535 |
|
| 536 |
class RiskCalculator:
|
src/analysis/sentiment_analysis.py
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.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()
|
src/core/__init__.py
ADDED
|
File without changes
|
src/core/log_parser.py
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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")
|
src/core/performance_monitor.py
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
import psutil
|
| 3 |
+
import threading
|
| 4 |
+
from datetime import datetime, timedelta
|
| 5 |
+
from typing import Dict, List, Any, Optional
|
| 6 |
+
from dataclasses import dataclass, asdict
|
| 7 |
+
from collections import deque, defaultdict
|
| 8 |
+
import json
|
| 9 |
+
import logging
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
@dataclass
|
| 13 |
+
class PerformanceMetrics:
|
| 14 |
+
"""Métricas de performance do sistema"""
|
| 15 |
+
timestamp: datetime
|
| 16 |
+
cpu_usage: float
|
| 17 |
+
memory_usage: float
|
| 18 |
+
memory_available: float
|
| 19 |
+
disk_usage: float
|
| 20 |
+
analysis_time: float = 0.0
|
| 21 |
+
events_processed: int = 0
|
| 22 |
+
errors_count: int = 0
|
| 23 |
+
bot_signals_count: int = 0
|
| 24 |
+
fibonacci_alerts_count: int = 0
|
| 25 |
+
|
| 26 |
+
@dataclass
|
| 27 |
+
class BotPerformanceStats:
|
| 28 |
+
"""Estatísticas de performance específicas do bot"""
|
| 29 |
+
total_analyses: int = 0
|
| 30 |
+
successful_analyses: int = 0
|
| 31 |
+
failed_analyses: int = 0
|
| 32 |
+
average_analysis_time: float = 0.0
|
| 33 |
+
signals_generated: Dict[str, int] = None
|
| 34 |
+
fibonacci_accuracy: float = 0.0
|
| 35 |
+
last_update: datetime = None
|
| 36 |
+
|
| 37 |
+
def __post_init__(self):
|
| 38 |
+
if self.signals_generated is None:
|
| 39 |
+
self.signals_generated = {'COMPRAR': 0, 'VENDER': 0, 'AGUARDAR': 0}
|
| 40 |
+
if self.last_update is None:
|
| 41 |
+
self.last_update = datetime.now()
|
| 42 |
+
|
| 43 |
+
class PerformanceMonitor:
|
| 44 |
+
"""Monitor de performance do sistema e bot"""
|
| 45 |
+
|
| 46 |
+
def __init__(self, max_metrics_history: int = 1000):
|
| 47 |
+
self.max_metrics_history = max_metrics_history
|
| 48 |
+
self.metrics_history = deque(maxlen=max_metrics_history)
|
| 49 |
+
self.bot_stats = BotPerformanceStats()
|
| 50 |
+
self.analysis_times = deque(maxlen=100) # Últimos 100 tempos de análise
|
| 51 |
+
self.error_log = deque(maxlen=50) # Últimos 50 erros
|
| 52 |
+
|
| 53 |
+
self.monitoring = False
|
| 54 |
+
self.monitor_thread = None
|
| 55 |
+
self.lock = threading.Lock()
|
| 56 |
+
|
| 57 |
+
# Setup logging
|
| 58 |
+
self.logger = logging.getLogger(__name__)
|
| 59 |
+
|
| 60 |
+
# Alertas de performance
|
| 61 |
+
self.performance_alerts = {
|
| 62 |
+
'high_cpu': {'threshold': 80.0, 'triggered': False},
|
| 63 |
+
'high_memory': {'threshold': 85.0, 'triggered': False},
|
| 64 |
+
'slow_analysis': {'threshold': 5.0, 'triggered': False}, # segundos
|
| 65 |
+
'high_error_rate': {'threshold': 0.1, 'triggered': False} # 10%
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
def start_monitoring(self, interval: float = 5.0):
|
| 69 |
+
"""Inicia o monitoramento de performance"""
|
| 70 |
+
if self.monitoring:
|
| 71 |
+
return
|
| 72 |
+
|
| 73 |
+
self.monitoring = True
|
| 74 |
+
self.monitor_thread = threading.Thread(
|
| 75 |
+
target=self._monitoring_loop,
|
| 76 |
+
args=(interval,),
|
| 77 |
+
daemon=True
|
| 78 |
+
)
|
| 79 |
+
self.monitor_thread.start()
|
| 80 |
+
self.logger.info(f"Monitoramento de performance iniciado (intervalo: {interval}s)")
|
| 81 |
+
|
| 82 |
+
def stop_monitoring(self):
|
| 83 |
+
"""Para o monitoramento de performance"""
|
| 84 |
+
self.monitoring = False
|
| 85 |
+
if self.monitor_thread:
|
| 86 |
+
self.monitor_thread.join(timeout=1.0)
|
| 87 |
+
self.logger.info("Monitoramento de performance parado")
|
| 88 |
+
|
| 89 |
+
def _monitoring_loop(self, interval: float):
|
| 90 |
+
"""Loop principal de monitoramento"""
|
| 91 |
+
while self.monitoring:
|
| 92 |
+
try:
|
| 93 |
+
metrics = self._collect_system_metrics()
|
| 94 |
+
|
| 95 |
+
with self.lock:
|
| 96 |
+
self.metrics_history.append(metrics)
|
| 97 |
+
|
| 98 |
+
# Verificar alertas
|
| 99 |
+
self._check_performance_alerts(metrics)
|
| 100 |
+
|
| 101 |
+
time.sleep(interval)
|
| 102 |
+
|
| 103 |
+
except Exception as e:
|
| 104 |
+
self.logger.error(f"Erro no monitoramento: {e}")
|
| 105 |
+
time.sleep(interval)
|
| 106 |
+
|
| 107 |
+
def _collect_system_metrics(self) -> PerformanceMetrics:
|
| 108 |
+
"""Coleta métricas do sistema"""
|
| 109 |
+
# CPU
|
| 110 |
+
cpu_usage = psutil.cpu_percent(interval=0.1)
|
| 111 |
+
|
| 112 |
+
# Memória
|
| 113 |
+
memory = psutil.virtual_memory()
|
| 114 |
+
memory_usage = memory.percent
|
| 115 |
+
memory_available = memory.available / (1024**3) # GB
|
| 116 |
+
|
| 117 |
+
# Disco
|
| 118 |
+
disk = psutil.disk_usage('/')
|
| 119 |
+
disk_usage = disk.percent
|
| 120 |
+
|
| 121 |
+
return PerformanceMetrics(
|
| 122 |
+
timestamp=datetime.now(),
|
| 123 |
+
cpu_usage=cpu_usage,
|
| 124 |
+
memory_usage=memory_usage,
|
| 125 |
+
memory_available=memory_available,
|
| 126 |
+
disk_usage=disk_usage
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
def record_analysis_time(self, analysis_time: float):
|
| 130 |
+
"""Registra tempo de análise"""
|
| 131 |
+
with self.lock:
|
| 132 |
+
self.analysis_times.append(analysis_time)
|
| 133 |
+
self.bot_stats.total_analyses += 1
|
| 134 |
+
|
| 135 |
+
# Atualizar tempo médio
|
| 136 |
+
if self.analysis_times:
|
| 137 |
+
self.bot_stats.average_analysis_time = sum(self.analysis_times) / len(self.analysis_times)
|
| 138 |
+
|
| 139 |
+
def record_successful_analysis(self, signal: str = None):
|
| 140 |
+
"""Registra análise bem-sucedida"""
|
| 141 |
+
with self.lock:
|
| 142 |
+
self.bot_stats.successful_analyses += 1
|
| 143 |
+
|
| 144 |
+
if signal and signal in self.bot_stats.signals_generated:
|
| 145 |
+
self.bot_stats.signals_generated[signal] += 1
|
| 146 |
+
|
| 147 |
+
self.bot_stats.last_update = datetime.now()
|
| 148 |
+
|
| 149 |
+
def record_failed_analysis(self, error: str):
|
| 150 |
+
"""Registra análise falhada"""
|
| 151 |
+
with self.lock:
|
| 152 |
+
self.bot_stats.failed_analyses += 1
|
| 153 |
+
self.error_log.append({
|
| 154 |
+
'timestamp': datetime.now(),
|
| 155 |
+
'error': error
|
| 156 |
+
})
|
| 157 |
+
self.bot_stats.last_update = datetime.now()
|
| 158 |
+
|
| 159 |
+
def record_fibonacci_alert(self):
|
| 160 |
+
"""Registra alerta de Fibonacci"""
|
| 161 |
+
with self.lock:
|
| 162 |
+
self.bot_stats.fibonacci_alerts_count += 1
|
| 163 |
+
|
| 164 |
+
def _check_performance_alerts(self, metrics: PerformanceMetrics):
|
| 165 |
+
"""Verifica alertas de performance"""
|
| 166 |
+
# CPU alto
|
| 167 |
+
if metrics.cpu_usage > self.performance_alerts['high_cpu']['threshold']:
|
| 168 |
+
if not self.performance_alerts['high_cpu']['triggered']:
|
| 169 |
+
self.logger.warning(f"ALERTA: CPU alto ({metrics.cpu_usage:.1f}%)")
|
| 170 |
+
self.performance_alerts['high_cpu']['triggered'] = True
|
| 171 |
+
else:
|
| 172 |
+
self.performance_alerts['high_cpu']['triggered'] = False
|
| 173 |
+
|
| 174 |
+
# Memória alta
|
| 175 |
+
if metrics.memory_usage > self.performance_alerts['high_memory']['threshold']:
|
| 176 |
+
if not self.performance_alerts['high_memory']['triggered']:
|
| 177 |
+
self.logger.warning(f"ALERTA: Memória alta ({metrics.memory_usage:.1f}%)")
|
| 178 |
+
self.performance_alerts['high_memory']['triggered'] = True
|
| 179 |
+
else:
|
| 180 |
+
self.performance_alerts['high_memory']['triggered'] = False
|
| 181 |
+
|
| 182 |
+
# Análise lenta
|
| 183 |
+
if self.analysis_times and self.bot_stats.average_analysis_time > self.performance_alerts['slow_analysis']['threshold']:
|
| 184 |
+
if not self.performance_alerts['slow_analysis']['triggered']:
|
| 185 |
+
self.logger.warning(f"ALERTA: Análise lenta ({self.bot_stats.average_analysis_time:.2f}s)")
|
| 186 |
+
self.performance_alerts['slow_analysis']['triggered'] = True
|
| 187 |
+
else:
|
| 188 |
+
self.performance_alerts['slow_analysis']['triggered'] = False
|
| 189 |
+
|
| 190 |
+
# Taxa de erro alta
|
| 191 |
+
if self.bot_stats.total_analyses > 0:
|
| 192 |
+
error_rate = self.bot_stats.failed_analyses / self.bot_stats.total_analyses
|
| 193 |
+
if error_rate > self.performance_alerts['high_error_rate']['threshold']:
|
| 194 |
+
if not self.performance_alerts['high_error_rate']['triggered']:
|
| 195 |
+
self.logger.warning(f"ALERTA: Taxa de erro alta ({error_rate:.1%})")
|
| 196 |
+
self.performance_alerts['high_error_rate']['triggered'] = True
|
| 197 |
+
else:
|
| 198 |
+
self.performance_alerts['high_error_rate']['triggered'] = False
|
| 199 |
+
|
| 200 |
+
def get_current_metrics(self) -> Optional[PerformanceMetrics]:
|
| 201 |
+
"""Retorna métricas atuais"""
|
| 202 |
+
with self.lock:
|
| 203 |
+
return self.metrics_history[-1] if self.metrics_history else None
|
| 204 |
+
|
| 205 |
+
def get_metrics_history(self, minutes: int = 60) -> List[PerformanceMetrics]:
|
| 206 |
+
"""Retorna histórico de métricas dos últimos N minutos"""
|
| 207 |
+
cutoff_time = datetime.now() - timedelta(minutes=minutes)
|
| 208 |
+
|
| 209 |
+
with self.lock:
|
| 210 |
+
return [m for m in self.metrics_history if m.timestamp >= cutoff_time]
|
| 211 |
+
|
| 212 |
+
def get_bot_stats(self) -> BotPerformanceStats:
|
| 213 |
+
"""Retorna estatísticas do bot"""
|
| 214 |
+
with self.lock:
|
| 215 |
+
return BotPerformanceStats(
|
| 216 |
+
total_analyses=self.bot_stats.total_analyses,
|
| 217 |
+
successful_analyses=self.bot_stats.successful_analyses,
|
| 218 |
+
failed_analyses=self.bot_stats.failed_analyses,
|
| 219 |
+
average_analysis_time=self.bot_stats.average_analysis_time,
|
| 220 |
+
signals_generated=self.bot_stats.signals_generated.copy(),
|
| 221 |
+
fibonacci_accuracy=self.bot_stats.fibonacci_accuracy,
|
| 222 |
+
last_update=self.bot_stats.last_update
|
| 223 |
+
)
|
| 224 |
+
|
| 225 |
+
def get_performance_summary(self) -> Dict[str, Any]:
|
| 226 |
+
"""Retorna resumo de performance"""
|
| 227 |
+
current_metrics = self.get_current_metrics()
|
| 228 |
+
bot_stats = self.get_bot_stats()
|
| 229 |
+
|
| 230 |
+
# Calcular estatísticas dos últimos 30 minutos
|
| 231 |
+
recent_metrics = self.get_metrics_history(30)
|
| 232 |
+
|
| 233 |
+
avg_cpu = sum(m.cpu_usage for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0
|
| 234 |
+
avg_memory = sum(m.memory_usage for m in recent_metrics) / len(recent_metrics) if recent_metrics else 0
|
| 235 |
+
|
| 236 |
+
# Taxa de sucesso
|
| 237 |
+
success_rate = 0
|
| 238 |
+
if bot_stats.total_analyses > 0:
|
| 239 |
+
success_rate = bot_stats.successful_analyses / bot_stats.total_analyses
|
| 240 |
+
|
| 241 |
+
# Alertas ativos
|
| 242 |
+
active_alerts = [name for name, alert in self.performance_alerts.items() if alert['triggered']]
|
| 243 |
+
|
| 244 |
+
return {
|
| 245 |
+
'current_metrics': asdict(current_metrics) if current_metrics else None,
|
| 246 |
+
'bot_stats': asdict(bot_stats),
|
| 247 |
+
'averages_30min': {
|
| 248 |
+
'cpu_usage': avg_cpu,
|
| 249 |
+
'memory_usage': avg_memory
|
| 250 |
+
},
|
| 251 |
+
'success_rate': success_rate,
|
| 252 |
+
'active_alerts': active_alerts,
|
| 253 |
+
'recent_errors': list(self.error_log)[-5:], # Últimos 5 erros
|
| 254 |
+
'monitoring_status': self.monitoring
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
def export_metrics(self, filepath: str, hours: int = 24):
|
| 258 |
+
"""Exporta métricas para arquivo JSON"""
|
| 259 |
+
metrics_data = self.get_metrics_history(hours * 60)
|
| 260 |
+
bot_stats = self.get_bot_stats()
|
| 261 |
+
|
| 262 |
+
export_data = {
|
| 263 |
+
'export_timestamp': datetime.now().isoformat(),
|
| 264 |
+
'metrics_history': [asdict(m) for m in metrics_data],
|
| 265 |
+
'bot_statistics': asdict(bot_stats),
|
| 266 |
+
'performance_summary': self.get_performance_summary()
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
# Converter datetime para string
|
| 270 |
+
def datetime_converter(obj):
|
| 271 |
+
if isinstance(obj, datetime):
|
| 272 |
+
return obj.isoformat()
|
| 273 |
+
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
|
| 274 |
+
|
| 275 |
+
with open(filepath, 'w', encoding='utf-8') as f:
|
| 276 |
+
json.dump(export_data, f, indent=2, default=datetime_converter, ensure_ascii=False)
|
| 277 |
+
|
| 278 |
+
self.logger.info(f"Métricas exportadas para: {filepath}")
|
| 279 |
+
|
| 280 |
+
def reset_stats(self):
|
| 281 |
+
"""Reseta estatísticas do bot"""
|
| 282 |
+
with self.lock:
|
| 283 |
+
self.bot_stats = BotPerformanceStats()
|
| 284 |
+
self.analysis_times.clear()
|
| 285 |
+
self.error_log.clear()
|
| 286 |
+
|
| 287 |
+
self.logger.info("Estatísticas resetadas")
|
| 288 |
+
|
| 289 |
+
def optimize_performance(self) -> List[str]:
|
| 290 |
+
"""Sugere otimizações baseadas nas métricas"""
|
| 291 |
+
suggestions = []
|
| 292 |
+
current_metrics = self.get_current_metrics()
|
| 293 |
+
bot_stats = self.get_bot_stats()
|
| 294 |
+
|
| 295 |
+
if current_metrics:
|
| 296 |
+
# CPU alto
|
| 297 |
+
if current_metrics.cpu_usage > 70:
|
| 298 |
+
suggestions.append("CPU alto detectado. Considere reduzir a frequência de análise.")
|
| 299 |
+
|
| 300 |
+
# Memória alta
|
| 301 |
+
if current_metrics.memory_usage > 80:
|
| 302 |
+
suggestions.append("Uso de memória alto. Considere limpar cache ou reduzir histórico.")
|
| 303 |
+
|
| 304 |
+
# Análise lenta
|
| 305 |
+
if bot_stats.average_analysis_time > 3.0:
|
| 306 |
+
suggestions.append("Análises lentas detectadas. Verifique a complexidade dos cálculos.")
|
| 307 |
+
|
| 308 |
+
# Taxa de erro alta
|
| 309 |
+
if bot_stats.total_analyses > 10:
|
| 310 |
+
error_rate = bot_stats.failed_analyses / bot_stats.total_analyses
|
| 311 |
+
if error_rate > 0.05: # 5%
|
| 312 |
+
suggestions.append(f"Taxa de erro alta ({error_rate:.1%}). Verifique logs de erro.")
|
| 313 |
+
|
| 314 |
+
if not suggestions:
|
| 315 |
+
suggestions.append("Sistema operando dentro dos parâmetros normais.")
|
| 316 |
+
|
| 317 |
+
return suggestions
|
| 318 |
+
|
| 319 |
+
# Instância global do monitor
|
| 320 |
+
performance_monitor = PerformanceMonitor()
|
| 321 |
+
|
| 322 |
+
# Decorador para medir tempo de análise
|
| 323 |
+
def measure_analysis_time(func):
|
| 324 |
+
"""Decorador para medir tempo de análise"""
|
| 325 |
+
def wrapper(*args, **kwargs):
|
| 326 |
+
start_time = time.time()
|
| 327 |
+
try:
|
| 328 |
+
result = func(*args, **kwargs)
|
| 329 |
+
analysis_time = time.time() - start_time
|
| 330 |
+
performance_monitor.record_analysis_time(analysis_time)
|
| 331 |
+
performance_monitor.record_successful_analysis()
|
| 332 |
+
return result
|
| 333 |
+
except Exception as e:
|
| 334 |
+
analysis_time = time.time() - start_time
|
| 335 |
+
performance_monitor.record_analysis_time(analysis_time)
|
| 336 |
+
performance_monitor.record_failed_analysis(str(e))
|
| 337 |
+
raise
|
| 338 |
+
return wrapper
|
| 339 |
+
|
| 340 |
+
# Exemplo de uso
|
| 341 |
+
if __name__ == "__main__":
|
| 342 |
+
# Configurar logging
|
| 343 |
+
logging.basicConfig(level=logging.INFO)
|
| 344 |
+
|
| 345 |
+
# Iniciar monitoramento
|
| 346 |
+
monitor = PerformanceMonitor()
|
| 347 |
+
monitor.start_monitoring(interval=2.0)
|
| 348 |
+
|
| 349 |
+
try:
|
| 350 |
+
# Simular algumas análises
|
| 351 |
+
for i in range(10):
|
| 352 |
+
time.sleep(1)
|
| 353 |
+
monitor.record_analysis_time(0.5 + i * 0.1)
|
| 354 |
+
if i % 3 == 0:
|
| 355 |
+
monitor.record_successful_analysis('COMPRAR')
|
| 356 |
+
else:
|
| 357 |
+
monitor.record_successful_analysis('AGUARDAR')
|
| 358 |
+
|
| 359 |
+
# Mostrar resumo
|
| 360 |
+
summary = monitor.get_performance_summary()
|
| 361 |
+
print(json.dumps(summary, indent=2, default=str))
|
| 362 |
+
|
| 363 |
+
except KeyboardInterrupt:
|
| 364 |
+
print("Parando monitor...")
|
| 365 |
+
finally:
|
| 366 |
+
monitor.stop_monitoring()
|
src/integrations/__init__.py
ADDED
|
File without changes
|
src/integrations/real_time_integration.py
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 src.core.log_parser import VampireBotLogParser, BotAnalysis
|
| 14 |
+
from src.analysis.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()
|
src/ui/__init__.py
ADDED
|
File without changes
|
src/ui/__pycache__/gradio_interface.cpython-313.pyc
ADDED
|
Binary file (39.2 kB). View file
|
|
|
src/ui/gradio_interface.py
ADDED
|
@@ -0,0 +1,828 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.config import UIConfig, AppConfig
|
| 7 |
+
from src.utils.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 |
+
"""Cria o cabeçalho da aplicação."""
|
| 23 |
+
return f"""
|
| 24 |
+
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; margin-bottom: 20px;">
|
| 25 |
+
<h1 style="color: white; margin: 0; font-size: 2.5em; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">
|
| 26 |
+
📈 {AppConfig.APP_TITLE}
|
| 27 |
+
</h1>
|
| 28 |
+
<p style="color: #f0f0f0; margin: 10px 0 0 0; font-size: 1.2em;">
|
| 29 |
+
{AppConfig.APP_DESCRIPTION}
|
| 30 |
+
</p>
|
| 31 |
+
</div>
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
@staticmethod
|
| 35 |
+
def format_harmonic_patterns(analysis_result: Dict[str, Any]) -> str:
|
| 36 |
+
"""Formata padrões harmônicos detectados."""
|
| 37 |
+
harmonic_patterns = analysis_result.get('harmonic_patterns', [])
|
| 38 |
+
|
| 39 |
+
if not harmonic_patterns:
|
| 40 |
+
return """
|
| 41 |
+
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 42 |
+
<h3 style="color: #495057; margin-top: 0;">🎵 Padrões Harmônicos</h3>
|
| 43 |
+
<div style="text-align: center; padding: 30px; color: #6c757d;">
|
| 44 |
+
<div style="font-size: 2em; margin-bottom: 10px;">📊</div>
|
| 45 |
+
<p>Nenhum padrão harmônico detectado</p>
|
| 46 |
+
</div>
|
| 47 |
+
</div>
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
patterns_html = ""
|
| 51 |
+
for pattern in harmonic_patterns:
|
| 52 |
+
pattern_name = pattern.get('name', 'Desconhecido')
|
| 53 |
+
confidence = pattern.get('confidence', 0)
|
| 54 |
+
direction = pattern.get('direction', 'NEUTRO')
|
| 55 |
+
|
| 56 |
+
direction_emoji = "📈" if direction == "ALTA" else "📉" if direction == "BAIXA" else "➡️"
|
| 57 |
+
|
| 58 |
+
patterns_html += f"""
|
| 59 |
+
<div style="background: #f8f9fa; border-radius: 6px; padding: 15px; margin-bottom: 10px; border-left: 4px solid #007bff;">
|
| 60 |
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
| 61 |
+
<div>
|
| 62 |
+
<strong style="color: #495057;">{direction_emoji} {pattern_name}</strong>
|
| 63 |
+
<div style="color: #6c757d; font-size: 0.9em;">Direção: {direction}</div>
|
| 64 |
+
</div>
|
| 65 |
+
<div style="text-align: right;">
|
| 66 |
+
<div style="font-weight: bold; color: #007bff;">{confidence}%</div>
|
| 67 |
+
<div style="color: #6c757d; font-size: 0.8em;">Confiança</div>
|
| 68 |
+
</div>
|
| 69 |
+
</div>
|
| 70 |
+
</div>
|
| 71 |
+
"""
|
| 72 |
+
|
| 73 |
+
return f"""
|
| 74 |
+
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 75 |
+
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #007bff; padding-bottom: 10px;">
|
| 76 |
+
🎵 Padrões Harmônicos Detectados
|
| 77 |
+
</h3>
|
| 78 |
+
{patterns_html}
|
| 79 |
+
</div>
|
| 80 |
+
"""
|
| 81 |
+
|
| 82 |
+
@staticmethod
|
| 83 |
+
def format_fibonacci_alerts(analysis_result: Dict[str, Any]) -> str:
|
| 84 |
+
"""Formata alertas de Fibonacci."""
|
| 85 |
+
fibonacci_data = analysis_result.get('fibonacci_analysis', {})
|
| 86 |
+
|
| 87 |
+
if not fibonacci_data:
|
| 88 |
+
return """
|
| 89 |
+
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 90 |
+
<h3 style="color: #495057; margin-top: 0;">📐 Análise de Fibonacci</h3>
|
| 91 |
+
<div style="text-align: center; padding: 30px; color: #6c757d;">
|
| 92 |
+
<div style="font-size: 2em; margin-bottom: 10px;">📊</div>
|
| 93 |
+
<p>Dados de Fibonacci não disponíveis</p>
|
| 94 |
+
</div>
|
| 95 |
+
</div>
|
| 96 |
+
"""
|
| 97 |
+
|
| 98 |
+
levels = fibonacci_data.get('levels', [])
|
| 99 |
+
current_level = fibonacci_data.get('current_level', 'N/A')
|
| 100 |
+
support_resistance = fibonacci_data.get('support_resistance', {})
|
| 101 |
+
|
| 102 |
+
levels_html = ""
|
| 103 |
+
for level in levels:
|
| 104 |
+
level_value = level.get('level', 0)
|
| 105 |
+
price = level.get('price', 0)
|
| 106 |
+
status = level.get('status', 'NEUTRO')
|
| 107 |
+
|
| 108 |
+
status_color = {
|
| 109 |
+
'SUPORTE': '#28a745',
|
| 110 |
+
'RESISTENCIA': '#dc3545',
|
| 111 |
+
'NEUTRO': '#6c757d'
|
| 112 |
+
}.get(status, '#6c757d')
|
| 113 |
+
|
| 114 |
+
levels_html += f"""
|
| 115 |
+
<div style="display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #eee;">
|
| 116 |
+
<span style="color: #495057;">{level_value}%</span>
|
| 117 |
+
<span style="font-weight: bold; color: #495057;">{NumberUtils.format_price(price)}</span>
|
| 118 |
+
<span style="color: {status_color}; font-weight: 600;">{status}</span>
|
| 119 |
+
</div>
|
| 120 |
+
"""
|
| 121 |
+
|
| 122 |
+
return f"""
|
| 123 |
+
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 124 |
+
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #ffc107; padding-bottom: 10px;">
|
| 125 |
+
📐 Análise de Fibonacci
|
| 126 |
+
</h3>
|
| 127 |
+
|
| 128 |
+
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; padding: 15px; margin-bottom: 15px;">
|
| 129 |
+
<strong style="color: #856404;">Nível Atual: {current_level}</strong>
|
| 130 |
+
</div>
|
| 131 |
+
|
| 132 |
+
<h4 style="color: #495057; margin-bottom: 10px;">📊 Níveis de Fibonacci</h4>
|
| 133 |
+
<div style="background: #f8f9fa; border-radius: 6px; padding: 15px;">
|
| 134 |
+
<div style="display: flex; justify-content: space-between; font-weight: bold; color: #495057; border-bottom: 2px solid #dee2e6; padding-bottom: 8px; margin-bottom: 10px;">
|
| 135 |
+
<span>Nível</span>
|
| 136 |
+
<span>Preço</span>
|
| 137 |
+
<span>Status</span>
|
| 138 |
+
</div>
|
| 139 |
+
{levels_html}
|
| 140 |
+
</div>
|
| 141 |
+
</div>
|
| 142 |
+
"""
|
| 143 |
+
|
| 144 |
+
@staticmethod
|
| 145 |
+
def format_bot_analysis_result(analysis_result: Dict[str, Any]) -> str:
|
| 146 |
+
"""Formata resultado específico da análise do bot externo."""
|
| 147 |
+
try:
|
| 148 |
+
bot_data = analysis_result.get('bot_data', {})
|
| 149 |
+
fibonacci_alerts = bot_data.get('fibonacci_alerts', 0)
|
| 150 |
+
fibonacci_signal = bot_data.get('fibonacci_signal', 'UNKNOWN')
|
| 151 |
+
technical_indicators = bot_data.get('technical_indicators', {})
|
| 152 |
+
|
| 153 |
+
# Formatação do sinal Fibonacci
|
| 154 |
+
signal_color = {
|
| 155 |
+
'BUY': '#28a745',
|
| 156 |
+
'SELL': '#dc3545',
|
| 157 |
+
'HOLD': '#ffc107',
|
| 158 |
+
'UNKNOWN': '#6c757d'
|
| 159 |
+
}.get(fibonacci_signal, '#6c757d')
|
| 160 |
+
|
| 161 |
+
signal_emoji = {
|
| 162 |
+
'BUY': '📈',
|
| 163 |
+
'SELL': '📉',
|
| 164 |
+
'HOLD': '⏸️',
|
| 165 |
+
'UNKNOWN': '❓'
|
| 166 |
+
}.get(fibonacci_signal, '❓')
|
| 167 |
+
|
| 168 |
+
# Indicadores técnicos do bot
|
| 169 |
+
indicators_html = ""
|
| 170 |
+
for indicator, value in technical_indicators.items():
|
| 171 |
+
if value is not None:
|
| 172 |
+
indicators_html += f"""
|
| 173 |
+
<div style="display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #eee;">
|
| 174 |
+
<span style="color: #495057; text-transform: uppercase;">{indicator}</span>
|
| 175 |
+
<span style="font-weight: bold; color: #495057;">{value}</span>
|
| 176 |
+
</div>
|
| 177 |
+
"""
|
| 178 |
+
|
| 179 |
+
return f"""
|
| 180 |
+
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; padding: 25px; color: white; margin-bottom: 20px;">
|
| 181 |
+
<h2 style="margin-top: 0; display: flex; align-items: center; gap: 10px;">
|
| 182 |
+
🤖 Análise do Bot de Trading
|
| 183 |
+
</h2>
|
| 184 |
+
|
| 185 |
+
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px;">
|
| 186 |
+
<div style="background: rgba(255,255,255,0.1); border-radius: 8px; padding: 15px;">
|
| 187 |
+
<h4 style="margin-top: 0; color: #f8f9fa;">📐 Fibonacci</h4>
|
| 188 |
+
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
|
| 189 |
+
<span style="font-size: 1.5em;">{signal_emoji}</span>
|
| 190 |
+
<div>
|
| 191 |
+
<div style="font-weight: bold; font-size: 1.1em; color: {signal_color};">{fibonacci_signal}</div>
|
| 192 |
+
<div style="font-size: 0.9em; opacity: 0.8;">Sinal Atual</div>
|
| 193 |
+
</div>
|
| 194 |
+
</div>
|
| 195 |
+
<div style="font-size: 0.9em; opacity: 0.8;">Alertas: {fibonacci_alerts}</div>
|
| 196 |
+
</div>
|
| 197 |
+
|
| 198 |
+
<div style="background: rgba(255,255,255,0.1); border-radius: 8px; padding: 15px;">
|
| 199 |
+
<h4 style="margin-top: 0; color: #f8f9fa;">📊 Indicadores</h4>
|
| 200 |
+
<div style="font-size: 0.9em;">
|
| 201 |
+
{indicators_html if indicators_html else '<div style="opacity: 0.8;">Dados não disponíveis</div>'}
|
| 202 |
+
</div>
|
| 203 |
+
</div>
|
| 204 |
+
</div>
|
| 205 |
+
</div>
|
| 206 |
+
|
| 207 |
+
{ResultFormatter.format_main_result(analysis_result)}
|
| 208 |
+
"""
|
| 209 |
+
|
| 210 |
+
except Exception as e:
|
| 211 |
+
return f"""
|
| 212 |
+
<div style="background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 8px; padding: 15px; color: #721c24;">
|
| 213 |
+
<h4 style="margin-top: 0;">❌ Erro na Formatação</h4>
|
| 214 |
+
<p style="margin: 0;">Erro ao formatar resultado do bot: {str(e)}</p>
|
| 215 |
+
</div>
|
| 216 |
+
"""
|
| 217 |
+
|
| 218 |
+
@staticmethod
|
| 219 |
+
def create_header() -> str:
|
| 220 |
+
"""Cria cabeçalho da aplicação."""
|
| 221 |
+
return f"""
|
| 222 |
+
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; margin-bottom: 20px;">
|
| 223 |
+
<h1 style="color: white; margin: 0; font-size: 2.5em; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">
|
| 224 |
+
📈 {AppConfig.APP_TITLE}
|
| 225 |
+
</h1>
|
| 226 |
+
<p style="color: #f0f0f0; margin: 10px 0 0 0; font-size: 1.2em;">
|
| 227 |
+
{AppConfig.APP_DESCRIPTION}
|
| 228 |
+
</p>
|
| 229 |
+
</div>
|
| 230 |
+
"""
|
| 231 |
+
|
| 232 |
+
@staticmethod
|
| 233 |
+
def create_input_section() -> gr.Column:
|
| 234 |
+
"""Cria seção de entrada de dados."""
|
| 235 |
+
with gr.Column() as input_section:
|
| 236 |
+
gr.HTML("""
|
| 237 |
+
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; border-left: 4px solid #007bff;">
|
| 238 |
+
<h3 style="margin: 0 0 10px 0; color: #495057;">📊 Dados de Mercado</h3>
|
| 239 |
+
<p style="margin: 0; color: #6c757d; font-size: 0.9em;">
|
| 240 |
+
Cole os dados do ativo ou digite manualmente os valores
|
| 241 |
+
</p>
|
| 242 |
+
</div>
|
| 243 |
+
""")
|
| 244 |
+
|
| 245 |
+
# Abas para diferentes tipos de entrada
|
| 246 |
+
with gr.Tabs():
|
| 247 |
+
with gr.Tab("📊 Análise Manual"):
|
| 248 |
+
symbol_input = gr.Textbox(
|
| 249 |
+
label="Símbolo do Ativo",
|
| 250 |
+
placeholder="Ex: BTCUSDT",
|
| 251 |
+
lines=1
|
| 252 |
+
)
|
| 253 |
+
price_input = gr.Number(
|
| 254 |
+
label="Preço Atual",
|
| 255 |
+
placeholder="Ex: 45000.50"
|
| 256 |
+
)
|
| 257 |
+
volume_input = gr.Number(
|
| 258 |
+
label="Volume (Opcional)",
|
| 259 |
+
placeholder="Ex: 1000000"
|
| 260 |
+
)
|
| 261 |
+
sentiment_input = gr.Textbox(
|
| 262 |
+
label="Texto para Análise de Sentimento (Opcional)",
|
| 263 |
+
placeholder="Ex: Notícias ou comentários sobre o ativo",
|
| 264 |
+
lines=3
|
| 265 |
+
)
|
| 266 |
+
analyze_manual_btn = gr.Button(
|
| 267 |
+
"🔍 Analisar Manualmente",
|
| 268 |
+
variant="primary",
|
| 269 |
+
size="lg"
|
| 270 |
+
)
|
| 271 |
+
|
| 272 |
+
with gr.Tab("🤖 Log do Bot"):
|
| 273 |
+
market_input = gr.Textbox(
|
| 274 |
+
label="Log do Bot de Trading",
|
| 275 |
+
placeholder=AppConfig.EXAMPLE_INPUT,
|
| 276 |
+
lines=8,
|
| 277 |
+
max_lines=15
|
| 278 |
+
)
|
| 279 |
+
analyze_btn = gr.Button(
|
| 280 |
+
"🔍 Analisar Log do Bot",
|
| 281 |
+
variant="primary",
|
| 282 |
+
size="lg"
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
return input_section, market_input, analyze_btn, symbol_input, price_input, volume_input, sentiment_input, analyze_manual_btn
|
| 286 |
+
|
| 287 |
+
@staticmethod
|
| 288 |
+
def create_output_section() -> Tuple[gr.Column, Dict[str, Any]]:
|
| 289 |
+
"""Cria seção de saída de resultados."""
|
| 290 |
+
outputs = {}
|
| 291 |
+
|
| 292 |
+
with gr.Column() as output_section:
|
| 293 |
+
# Status do modelo de IA
|
| 294 |
+
outputs['ai_status'] = gr.HTML(
|
| 295 |
+
UIComponents._get_ai_status_html(available=False)
|
| 296 |
+
)
|
| 297 |
+
|
| 298 |
+
# Resultado principal
|
| 299 |
+
outputs['main_result'] = gr.HTML()
|
| 300 |
+
|
| 301 |
+
# Abas de detalhes
|
| 302 |
+
with gr.Tabs():
|
| 303 |
+
# Aba de Análise Técnica
|
| 304 |
+
with gr.Tab("📊 Análise Técnica"):
|
| 305 |
+
outputs['technical_analysis'] = gr.HTML()
|
| 306 |
+
|
| 307 |
+
# Aba de Análise de Sentimento
|
| 308 |
+
with gr.Tab("🧠 Análise de Sentimento"):
|
| 309 |
+
outputs['sentiment_analysis'] = gr.HTML()
|
| 310 |
+
|
| 311 |
+
# Aba de Recomendações
|
| 312 |
+
with gr.Tab("💡 Recomendações"):
|
| 313 |
+
outputs['recommendations'] = gr.HTML()
|
| 314 |
+
|
| 315 |
+
# Aba de Padrões Harmônicos
|
| 316 |
+
with gr.Tab("🎵 Padrões Harmônicos"):
|
| 317 |
+
outputs['harmonic_patterns'] = gr.HTML()
|
| 318 |
+
|
| 319 |
+
# Aba de Alertas Fibonacci
|
| 320 |
+
with gr.Tab("📐 Fibonacci"):
|
| 321 |
+
outputs['fibonacci_alerts'] = gr.HTML()
|
| 322 |
+
|
| 323 |
+
# Aba de Dados Brutos
|
| 324 |
+
with gr.Tab("🔍 Dados Detalhados"):
|
| 325 |
+
outputs['raw_data'] = gr.JSON()
|
| 326 |
+
|
| 327 |
+
# Adicionar novos outputs ao dicionário
|
| 328 |
+
if 'harmonic_patterns' not in outputs:
|
| 329 |
+
outputs['harmonic_patterns'] = gr.HTML()
|
| 330 |
+
if 'fibonacci_alerts' not in outputs:
|
| 331 |
+
outputs['fibonacci_alerts'] = gr.HTML()
|
| 332 |
+
|
| 333 |
+
return output_section, outputs
|
| 334 |
+
|
| 335 |
+
@staticmethod
|
| 336 |
+
def create_footer(model_info: Optional[Dict[str, Any]] = None) -> str:
|
| 337 |
+
"""Cria rodapé da aplicação."""
|
| 338 |
+
timestamp = DateTimeUtils.get_current_datetime()
|
| 339 |
+
|
| 340 |
+
if model_info and model_info.get('available', False):
|
| 341 |
+
ai_status = f"🤖 IA: {model_info.get('description', 'Modelo Ativo')}"
|
| 342 |
+
else:
|
| 343 |
+
ai_status = "⚠️ IA: Indisponível (apenas análise técnica)"
|
| 344 |
+
|
| 345 |
+
return f"""
|
| 346 |
+
<div style="text-align: center; padding: 15px; background: #f8f9fa; border-radius: 8px; margin-top: 20px; border-top: 2px solid #dee2e6;">
|
| 347 |
+
<p style="margin: 0; color: #6c757d; font-size: 0.9em;">
|
| 348 |
+
{ai_status} | ⏰ Última atualização: {timestamp}
|
| 349 |
+
</p>
|
| 350 |
+
<p style="margin: 5px 0 0 0; color: #adb5bd; font-size: 0.8em;">
|
| 351 |
+
Desenvolvido para análise de scalping no mercado financeiro
|
| 352 |
+
</p>
|
| 353 |
+
</div>
|
| 354 |
+
"""
|
| 355 |
+
|
| 356 |
+
@staticmethod
|
| 357 |
+
def _get_ai_status_html(available: bool, model_description: str = "") -> str:
|
| 358 |
+
"""Gera HTML para status da IA."""
|
| 359 |
+
if available:
|
| 360 |
+
return f"""
|
| 361 |
+
<div style="background: #d4edda; border: 1px solid #c3e6cb; border-radius: 8px; padding: 12px; margin-bottom: 15px;">
|
| 362 |
+
<div style="display: flex; align-items: center; gap: 10px;">
|
| 363 |
+
<span style="font-size: 1.2em;">🤖</span>
|
| 364 |
+
<div>
|
| 365 |
+
<strong style="color: #155724;">IA Ativa:</strong>
|
| 366 |
+
<span style="color: #155724;">{model_description}</span>
|
| 367 |
+
</div>
|
| 368 |
+
</div>
|
| 369 |
+
</div>
|
| 370 |
+
"""
|
| 371 |
+
else:
|
| 372 |
+
return """
|
| 373 |
+
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 12px; margin-bottom: 15px;">
|
| 374 |
+
<div style="display: flex; align-items: center; gap: 10px;">
|
| 375 |
+
<span style="font-size: 1.2em;">⚠️</span>
|
| 376 |
+
<div>
|
| 377 |
+
<strong style="color: #856404;">IA Indisponível:</strong>
|
| 378 |
+
<span style="color: #856404;">Executando apenas análise técnica</span>
|
| 379 |
+
</div>
|
| 380 |
+
</div>
|
| 381 |
+
</div>
|
| 382 |
+
"""
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
class ResultFormatter:
|
| 386 |
+
"""Formatador de resultados para a interface."""
|
| 387 |
+
|
| 388 |
+
@staticmethod
|
| 389 |
+
def format_main_result(analysis_result: Dict[str, Any]) -> str:
|
| 390 |
+
"""Formata resultado principal da análise."""
|
| 391 |
+
action = analysis_result.get('action', 'AGUARDAR')
|
| 392 |
+
confidence = analysis_result.get('confidence', 0)
|
| 393 |
+
market_data = analysis_result.get('market_data', {})
|
| 394 |
+
|
| 395 |
+
# Obter informações da ação
|
| 396 |
+
action_emojis = ActionUtils.get_action_emojis(action)
|
| 397 |
+
action_color = ActionUtils.get_action_color(action)
|
| 398 |
+
confidence_level = ConfidenceUtils.get_confidence_level(confidence)
|
| 399 |
+
confidence_bar = ConfidenceUtils.generate_confidence_bar(confidence)
|
| 400 |
+
|
| 401 |
+
# Formatação do preço
|
| 402 |
+
price = market_data.get('price', 0)
|
| 403 |
+
variation = market_data.get('variation', 0)
|
| 404 |
+
formatted_price = NumberUtils.format_price(price)
|
| 405 |
+
formatted_variation = NumberUtils.format_percentage(variation)
|
| 406 |
+
|
| 407 |
+
# Cor da variação
|
| 408 |
+
variation_color = "#28a745" if variation >= 0 else "#dc3545"
|
| 409 |
+
|
| 410 |
+
return f"""
|
| 411 |
+
<div style="background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-radius: 12px; padding: 25px; margin: 15px 0; border: 2px solid #dee2e6;">
|
| 412 |
+
<div style="text-align: center; margin-bottom: 20px;">
|
| 413 |
+
<div style="font-size: 3em; margin-bottom: 10px;">{action_emojis['main']}</div>
|
| 414 |
+
<h2 style="margin: 0; color: {action_color}; font-size: 2em; text-transform: uppercase; letter-spacing: 1px;">
|
| 415 |
+
{action_emojis['action']} {action}
|
| 416 |
+
</h2>
|
| 417 |
+
</div>
|
| 418 |
+
|
| 419 |
+
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
|
| 420 |
+
<div style="text-align: center; padding: 15px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
| 421 |
+
<div style="font-size: 0.9em; color: #6c757d; margin-bottom: 5px;">PREÇO ATUAL</div>
|
| 422 |
+
<div style="font-size: 1.8em; font-weight: bold; color: #495057;">{formatted_price}</div>
|
| 423 |
+
<div style="font-size: 1.1em; color: {variation_color}; font-weight: 600;">{formatted_variation}</div>
|
| 424 |
+
</div>
|
| 425 |
+
|
| 426 |
+
<div style="text-align: center; padding: 15px; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
| 427 |
+
<div style="font-size: 0.9em; color: #6c757d; margin-bottom: 5px;">CONFIANÇA</div>
|
| 428 |
+
<div style="font-size: 1.8em; font-weight: bold; color: #495057;">{confidence}%</div>
|
| 429 |
+
<div style="font-size: 0.9em; color: #6c757d;">{confidence_level}</div>
|
| 430 |
+
</div>
|
| 431 |
+
</div>
|
| 432 |
+
|
| 433 |
+
<div style="text-align: center; margin-top: 15px;">
|
| 434 |
+
<div style="font-size: 0.9em; color: #6c757d; margin-bottom: 8px;">NÍVEL DE CONFIANÇA</div>
|
| 435 |
+
<div style="font-family: monospace; font-size: 1.2em; letter-spacing: 2px; color: #495057;">{confidence_bar}</div>
|
| 436 |
+
</div>
|
| 437 |
+
</div>
|
| 438 |
+
"""
|
| 439 |
+
|
| 440 |
+
@staticmethod
|
| 441 |
+
def format_technical_analysis(analysis_result: Dict[str, Any]) -> str:
|
| 442 |
+
"""Formata análise técnica."""
|
| 443 |
+
market_data = analysis_result.get('market_data', {})
|
| 444 |
+
signals = analysis_result.get('signals', [])
|
| 445 |
+
|
| 446 |
+
# Resumo dos dados de mercado
|
| 447 |
+
market_summary = FormatUtils.format_market_summary(market_data)
|
| 448 |
+
|
| 449 |
+
# Lista de sinais
|
| 450 |
+
signals_list = FormatUtils.format_signal_list(signals)
|
| 451 |
+
|
| 452 |
+
return f"""
|
| 453 |
+
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 454 |
+
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #007bff; padding-bottom: 10px;">
|
| 455 |
+
📊 Indicadores Técnicos
|
| 456 |
+
</h3>
|
| 457 |
+
|
| 458 |
+
<div style="background: #f8f9fa; padding: 15px; border-radius: 6px; margin-bottom: 20px;">
|
| 459 |
+
{market_summary}
|
| 460 |
+
</div>
|
| 461 |
+
|
| 462 |
+
<h4 style="color: #495057; margin-bottom: 15px;">🎯 Sinais Detectados</h4>
|
| 463 |
+
<div style="background: #f8f9fa; padding: 15px; border-radius: 6px; font-family: monospace; white-space: pre-line;">
|
| 464 |
+
{signals_list}
|
| 465 |
+
</div>
|
| 466 |
+
</div>
|
| 467 |
+
"""
|
| 468 |
+
|
| 469 |
+
@staticmethod
|
| 470 |
+
def format_sentiment_analysis(analysis_result: Dict[str, Any]) -> str:
|
| 471 |
+
"""Formata análise de sentimento."""
|
| 472 |
+
sentiment = analysis_result.get('sentiment', {})
|
| 473 |
+
|
| 474 |
+
if not sentiment:
|
| 475 |
+
return """
|
| 476 |
+
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 477 |
+
<h3 style="color: #495057; margin-top: 0;">🧠 Análise de Sentimento</h3>
|
| 478 |
+
<div style="text-align: center; padding: 30px; color: #6c757d;">
|
| 479 |
+
<div style="font-size: 2em; margin-bottom: 10px;">⚠️</div>
|
| 480 |
+
<p>Análise de sentimento não disponível</p>
|
| 481 |
+
<p style="font-size: 0.9em;">Instale as dependências de IA para ativar esta funcionalidade</p>
|
| 482 |
+
</div>
|
| 483 |
+
</div>
|
| 484 |
+
"""
|
| 485 |
+
|
| 486 |
+
label = sentiment.get('label', 'NEUTRO')
|
| 487 |
+
confidence = sentiment.get('confidence', 0)
|
| 488 |
+
emoji = SentimentUtils.get_sentiment_emoji(label)
|
| 489 |
+
|
| 490 |
+
# Cor baseada no sentimento
|
| 491 |
+
sentiment_colors = {
|
| 492 |
+
'POSITIVO': '#28a745',
|
| 493 |
+
'NEGATIVO': '#dc3545',
|
| 494 |
+
'NEUTRO': '#ffc107'
|
| 495 |
+
}
|
| 496 |
+
color = sentiment_colors.get(label, '#6c757d')
|
| 497 |
+
|
| 498 |
+
return f"""
|
| 499 |
+
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 500 |
+
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #28a745; padding-bottom: 10px;">
|
| 501 |
+
🧠 Análise de Sentimento
|
| 502 |
+
</h3>
|
| 503 |
+
|
| 504 |
+
<div style="text-align: center; padding: 20px;">
|
| 505 |
+
<div style="font-size: 3em; margin-bottom: 15px;">{emoji}</div>
|
| 506 |
+
<h4 style="color: {color}; margin: 0; font-size: 1.5em; text-transform: uppercase;">{label}</h4>
|
| 507 |
+
<div style="margin-top: 10px; color: #6c757d;">Confiança: {confidence:.1f}%</div>
|
| 508 |
+
</div>
|
| 509 |
+
|
| 510 |
+
<div style="background: #f8f9fa; padding: 15px; border-radius: 6px; margin-top: 15px;">
|
| 511 |
+
<h5 style="margin-top: 0; color: #495057;">📝 Detalhes da Análise</h5>
|
| 512 |
+
<p style="margin: 0; color: #6c757d; font-size: 0.9em;">
|
| 513 |
+
O modelo de IA analisou o contexto do mercado e determinou um sentimento <strong>{label.lower()}</strong>
|
| 514 |
+
com {confidence:.1f}% de confiança.
|
| 515 |
+
</p>
|
| 516 |
+
</div>
|
| 517 |
+
</div>
|
| 518 |
+
"""
|
| 519 |
+
|
| 520 |
+
@staticmethod
|
| 521 |
+
def format_recommendations(analysis_result: Dict[str, Any]) -> str:
|
| 522 |
+
"""Formata recomendações de trading."""
|
| 523 |
+
action = analysis_result.get('action', 'AGUARDAR')
|
| 524 |
+
market_data = analysis_result.get('market_data', {})
|
| 525 |
+
price = market_data.get('price', 0)
|
| 526 |
+
|
| 527 |
+
# Recomendações de trading
|
| 528 |
+
trading_recs = FormatUtils.format_trading_recommendations(action, price)
|
| 529 |
+
|
| 530 |
+
# Direção de trading
|
| 531 |
+
direction = ActionUtils.get_trading_direction(action)
|
| 532 |
+
direction_emoji = "📈" if direction == "COMPRA" else "📉" if direction == "VENDA" else "⏸️"
|
| 533 |
+
|
| 534 |
+
return f"""
|
| 535 |
+
<div style="background: white; border-radius: 8px; padding: 20px; border: 1px solid #dee2e6;">
|
| 536 |
+
<h3 style="color: #495057; margin-top: 0; border-bottom: 2px solid #ffc107; padding-bottom: 10px;">
|
| 537 |
+
💡 Recomendações de Trading
|
| 538 |
+
</h3>
|
| 539 |
+
|
| 540 |
+
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 6px; padding: 15px; margin-bottom: 20px;">
|
| 541 |
+
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
|
| 542 |
+
<span style="font-size: 1.5em;">{direction_emoji}</span>
|
| 543 |
+
<strong style="color: #856404;">Direção: {direction}</strong>
|
| 544 |
+
</div>
|
| 545 |
+
</div>
|
| 546 |
+
|
| 547 |
+
<div style="background: #f8f9fa; padding: 15px; border-radius: 6px; white-space: pre-line;">
|
| 548 |
+
{trading_recs}
|
| 549 |
+
</div>
|
| 550 |
+
|
| 551 |
+
<div style="background: #d1ecf1; border: 1px solid #bee5eb; border-radius: 6px; padding: 15px; margin-top: 15px;">
|
| 552 |
+
<h5 style="margin-top: 0; color: #0c5460;">⚠️ Aviso Importante</h5>
|
| 553 |
+
<p style="margin: 0; color: #0c5460; font-size: 0.9em;">
|
| 554 |
+
Esta análise é apenas para fins educacionais. Sempre faça sua própria pesquisa e
|
| 555 |
+
considere consultar um consultor financeiro antes de tomar decisões de investimento.
|
| 556 |
+
</p>
|
| 557 |
+
</div>
|
| 558 |
+
</div>
|
| 559 |
+
"""
|
| 560 |
+
|
| 561 |
+
|
| 562 |
+
class GradioInterface:
|
| 563 |
+
"""Interface principal do Gradio."""
|
| 564 |
+
|
| 565 |
+
def __init__(self, analysis_function, model_info: Optional[Dict[str, Any]] = None):
|
| 566 |
+
"""Inicializa interface."""
|
| 567 |
+
self.analysis_function = analysis_function
|
| 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:
|
| 574 |
+
# Validar entrada
|
| 575 |
+
if not symbol or not price:
|
| 576 |
+
return "❌ Erro: Símbolo e preço são obrigatórios"
|
| 577 |
+
|
| 578 |
+
price = float(price)
|
| 579 |
+
volume = float(volume) if volume else 0
|
| 580 |
+
|
| 581 |
+
# Criar dados de mercado
|
| 582 |
+
market_data = {
|
| 583 |
+
'price': price,
|
| 584 |
+
'variation': 0, # Será calculado se necessário
|
| 585 |
+
'rsi': 50, # Valor padrão
|
| 586 |
+
'ema_trend': 'NEUTRO',
|
| 587 |
+
'bb_position': 'DENTRO',
|
| 588 |
+
'volume': volume
|
| 589 |
+
}
|
| 590 |
+
|
| 591 |
+
# Executar análise
|
| 592 |
+
result = self.analysis_function(f"{symbol}: {price}")
|
| 593 |
+
|
| 594 |
+
# Formatar resultado
|
| 595 |
+
return self.format_analysis_result(result, sentiment_text, symbol)
|
| 596 |
+
|
| 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:
|
| 603 |
+
if not log_content.strip():
|
| 604 |
+
return "❌ Erro: Conteúdo do log é obrigatório"
|
| 605 |
+
|
| 606 |
+
# Executar análise do log
|
| 607 |
+
result = self.analysis_function(log_content)
|
| 608 |
+
|
| 609 |
+
if 'error' in result:
|
| 610 |
+
return f"❌ {result['error']}"
|
| 611 |
+
|
| 612 |
+
# Formatar resultado específico do bot
|
| 613 |
+
return self.format_bot_analysis_result(result)
|
| 614 |
+
|
| 615 |
+
except Exception as e:
|
| 616 |
+
return f"❌ Erro na análise do log: {str(e)}"
|
| 617 |
+
|
| 618 |
+
def format_analysis_result(self, result, sentiment_text, symbol):
|
| 619 |
+
"""Formata resultado da análise"""
|
| 620 |
+
return ResultFormatter.format_main_result(result)
|
| 621 |
+
|
| 622 |
+
def format_bot_analysis_result(self, result):
|
| 623 |
+
"""Formata resultado específico da análise do bot"""
|
| 624 |
+
return ResultFormatter.format_main_result(result)
|
| 625 |
+
|
| 626 |
+
def create_interface(self) -> gr.Blocks:
|
| 627 |
+
"""Cria interface completa do Gradio."""
|
| 628 |
+
with gr.Blocks(
|
| 629 |
+
title=AppConfig.APP_TITLE,
|
| 630 |
+
theme=gr.themes.Soft(),
|
| 631 |
+
css=self._get_custom_css()
|
| 632 |
+
) as interface:
|
| 633 |
+
# Cabeçalho
|
| 634 |
+
gr.HTML(UIComponents.create_header())
|
| 635 |
+
|
| 636 |
+
# Layout principal
|
| 637 |
+
with gr.Row():
|
| 638 |
+
# Coluna de entrada (40%)
|
| 639 |
+
with gr.Column(scale=2):
|
| 640 |
+
input_section, market_input, analyze_btn, symbol_input, price_input, volume_input, sentiment_input, analyze_manual_btn = UIComponents.create_input_section()
|
| 641 |
+
|
| 642 |
+
# Coluna de saída (60%)
|
| 643 |
+
with gr.Column(scale=3):
|
| 644 |
+
output_section, outputs = UIComponents.create_output_section()
|
| 645 |
+
|
| 646 |
+
# Rodapé
|
| 647 |
+
gr.HTML(UIComponents.create_footer(self.model_info))
|
| 648 |
+
|
| 649 |
+
# Configurar eventos de análise
|
| 650 |
+
analyze_btn.click(
|
| 651 |
+
fn=self._analyze_wrapper,
|
| 652 |
+
inputs=[market_input],
|
| 653 |
+
outputs=[
|
| 654 |
+
outputs['ai_status'],
|
| 655 |
+
outputs['main_result'],
|
| 656 |
+
outputs['technical_analysis'],
|
| 657 |
+
outputs['sentiment_analysis'],
|
| 658 |
+
outputs['recommendations'],
|
| 659 |
+
outputs['harmonic_patterns'],
|
| 660 |
+
outputs['fibonacci_alerts'],
|
| 661 |
+
outputs['raw_data']
|
| 662 |
+
]
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
analyze_manual_btn.click(
|
| 666 |
+
fn=self._analyze_manual_wrapper,
|
| 667 |
+
inputs=[symbol_input, price_input, volume_input, sentiment_input],
|
| 668 |
+
outputs=[
|
| 669 |
+
outputs['ai_status'],
|
| 670 |
+
outputs['main_result'],
|
| 671 |
+
outputs['technical_analysis'],
|
| 672 |
+
outputs['sentiment_analysis'],
|
| 673 |
+
outputs['recommendations'],
|
| 674 |
+
outputs['harmonic_patterns'],
|
| 675 |
+
outputs['fibonacci_alerts'],
|
| 676 |
+
outputs['raw_data']
|
| 677 |
+
]
|
| 678 |
+
)
|
| 679 |
+
|
| 680 |
+
# Atualizar status da IA na inicialização
|
| 681 |
+
interface.load(
|
| 682 |
+
fn=lambda: UIComponents._get_ai_status_html(
|
| 683 |
+
self.model_info.get('available', False),
|
| 684 |
+
self.model_info.get('description', '')
|
| 685 |
+
),
|
| 686 |
+
outputs=[outputs['ai_status']]
|
| 687 |
+
)
|
| 688 |
+
|
| 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:
|
| 695 |
+
# Executar análise
|
| 696 |
+
analysis_result = self.analysis_function(market_input)
|
| 697 |
+
|
| 698 |
+
# Formatear resultados
|
| 699 |
+
ai_status = UIComponents._get_ai_status_html(
|
| 700 |
+
self.model_info.get('available', False),
|
| 701 |
+
self.model_info.get('description', '')
|
| 702 |
+
)
|
| 703 |
+
|
| 704 |
+
main_result = ResultFormatter.format_main_result(analysis_result)
|
| 705 |
+
technical_analysis = ResultFormatter.format_technical_analysis(analysis_result)
|
| 706 |
+
sentiment_analysis = ResultFormatter.format_sentiment_analysis(analysis_result)
|
| 707 |
+
recommendations = ResultFormatter.format_recommendations(analysis_result)
|
| 708 |
+
harmonic_patterns = ResultFormatter.format_harmonic_patterns(analysis_result)
|
| 709 |
+
fibonacci_alerts = ResultFormatter.format_fibonacci_alerts(analysis_result)
|
| 710 |
+
|
| 711 |
+
# Dados brutos para debug
|
| 712 |
+
raw_data = {
|
| 713 |
+
'timestamp': DateTimeUtils.get_current_datetime(),
|
| 714 |
+
'analysis_result': analysis_result
|
| 715 |
+
}
|
| 716 |
+
|
| 717 |
+
return (
|
| 718 |
+
ai_status,
|
| 719 |
+
main_result,
|
| 720 |
+
technical_analysis,
|
| 721 |
+
sentiment_analysis,
|
| 722 |
+
recommendations,
|
| 723 |
+
harmonic_patterns,
|
| 724 |
+
fibonacci_alerts,
|
| 725 |
+
raw_data
|
| 726 |
+
)
|
| 727 |
+
|
| 728 |
+
except Exception as e:
|
| 729 |
+
error_msg = f"Erro na análise: {str(e)}"
|
| 730 |
+
error_html = f"""
|
| 731 |
+
<div style="background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 8px; padding: 15px; color: #721c24;">
|
| 732 |
+
<h4 style="margin-top: 0;">❌ Erro na Análise</h4>
|
| 733 |
+
<p style="margin: 0;">{error_msg}</p>
|
| 734 |
+
</div>
|
| 735 |
+
"""
|
| 736 |
+
|
| 737 |
+
return (
|
| 738 |
+
UIComponents._get_ai_status_html(False),
|
| 739 |
+
error_html,
|
| 740 |
+
error_html,
|
| 741 |
+
error_html,
|
| 742 |
+
error_html,
|
| 743 |
+
error_html,
|
| 744 |
+
error_html,
|
| 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:
|
| 751 |
+
# Validar entrada
|
| 752 |
+
if not symbol or not price:
|
| 753 |
+
raise ValueError("Símbolo e preço são obrigatórios")
|
| 754 |
+
|
| 755 |
+
# Criar entrada formatada
|
| 756 |
+
market_input = f"{symbol}: Preço={price}, Volume={volume or 0}"
|
| 757 |
+
if sentiment_text:
|
| 758 |
+
market_input += f", Sentimento={sentiment_text}"
|
| 759 |
+
|
| 760 |
+
# Executar análise usando o wrapper padrão
|
| 761 |
+
return self._analyze_wrapper(market_input)
|
| 762 |
+
|
| 763 |
+
except Exception as e:
|
| 764 |
+
error_msg = f"Erro na análise manual: {str(e)}"
|
| 765 |
+
error_html = f"""
|
| 766 |
+
<div style="background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 8px; padding: 15px; color: #721c24;">
|
| 767 |
+
<h4 style="margin-top: 0;">❌ Erro na Análise Manual</h4>
|
| 768 |
+
<p style="margin: 0;">{error_msg}</p>
|
| 769 |
+
</div>
|
| 770 |
+
"""
|
| 771 |
+
|
| 772 |
+
return (
|
| 773 |
+
UIComponents._get_ai_status_html(False),
|
| 774 |
+
error_html,
|
| 775 |
+
error_html,
|
| 776 |
+
error_html,
|
| 777 |
+
error_html,
|
| 778 |
+
error_html,
|
| 779 |
+
error_html,
|
| 780 |
+
{'error': error_msg}
|
| 781 |
+
)
|
| 782 |
+
|
| 783 |
+
def _get_custom_css(self) -> str:
|
| 784 |
+
"""Retorna CSS customizado para a interface."""
|
| 785 |
+
return """
|
| 786 |
+
.gradio-container {
|
| 787 |
+
max-width: 1200px !important;
|
| 788 |
+
margin: auto !important;
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
.gr-button {
|
| 792 |
+
transition: all 0.3s ease !important;
|
| 793 |
+
}
|
| 794 |
+
|
| 795 |
+
.gr-button:hover {
|
| 796 |
+
transform: translateY(-2px) !important;
|
| 797 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.2) !important;
|
| 798 |
+
}
|
| 799 |
+
|
| 800 |
+
.gr-textbox textarea {
|
| 801 |
+
font-family: 'Courier New', monospace !important;
|
| 802 |
+
}
|
| 803 |
+
|
| 804 |
+
.gr-tab-nav {
|
| 805 |
+
background: #f8f9fa !important;
|
| 806 |
+
}
|
| 807 |
+
|
| 808 |
+
.gr-tab-nav button {
|
| 809 |
+
border-radius: 8px 8px 0 0 !important;
|
| 810 |
+
}
|
| 811 |
+
"""
|
| 812 |
+
|
| 813 |
+
def launch(self, **kwargs) -> None:
|
| 814 |
+
"""Lança a interface."""
|
| 815 |
+
if not self.interface:
|
| 816 |
+
self.create_interface()
|
| 817 |
+
|
| 818 |
+
default_kwargs = {
|
| 819 |
+
'server_name': '127.0.0.1',
|
| 820 |
+
'server_port': 7860,
|
| 821 |
+
'share': False,
|
| 822 |
+
'show_error': True
|
| 823 |
+
}
|
| 824 |
+
|
| 825 |
+
# Mesclar argumentos padrão com os fornecidos
|
| 826 |
+
launch_kwargs = {**default_kwargs, **kwargs}
|
| 827 |
+
|
| 828 |
+
self.interface.launch(**launch_kwargs)
|
src/utils/__init__.py
ADDED
|
File without changes
|
src/utils/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (141 Bytes). View file
|
|
|
src/utils/__pycache__/utils.cpython-313.pyc
ADDED
|
Binary file (18.2 kB). View file
|
|
|
src/utils/utils.py
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.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 |
+
}
|
tests/__init__.py
ADDED
|
File without changes
|
tests/integration/__init__.py
ADDED
|
File without changes
|
tests/unit/__init__.py
ADDED
|
File without changes
|
text
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
⏰ Análise #8 - 09:46:58
|
| 2 |
+
|
| 3 |
+
================================================================================
|
| 4 |
+
🧛 VAMPIRE TRADING BOT - ANÁLISE DETALHADA
|
| 5 |
+
================================================================================
|
| 6 |
+
|
| 7 |
+
📊 DADOS DE MERCADO - WINV25
|
| 8 |
+
──────────────────────────────────────────────────
|
| 9 |
+
Preço Atual: 140135.00000 ↗
|
| 10 |
+
Variação: +5.00000 (+0.00%)
|
| 11 |
+
Máxima: 140155.00000
|
| 12 |
+
Mínima: 140075.00000
|
| 13 |
+
Volume: 5023
|
| 14 |
+
|
| 15 |
+
📈 INDICADORES TÉCNICOS
|
| 16 |
+
──────────────────────────────────────────────────
|
| 17 |
+
RSI (14): 46.39 (NEUTRO)
|
| 18 |
+
EMA Rápida: 140192.30752
|
| 19 |
+
EMA Lenta: 140221.86717
|
| 20 |
+
Tendência EMA: BAIXA
|
| 21 |
+
Bollinger: DENTRO DAS BANDAS
|
| 22 |
+
Superior: 140672.37317
|
| 23 |
+
Inferior: 139913.62683
|
| 24 |
+
ATR: 170.73782
|
| 25 |
+
Volatilidade: MÉDIA (1.23x)
|
| 26 |
+
2025-08-27 09:46:58,218 - src.core.analysis.advanced_fibonacci - INFO - 🔮 INICIANDO ANÁLISE AVANÇADA DE FIBONACCI - Lookback: 40 períodos
|
| 27 |
+
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
|
| 28 |
+
2025-08-27 09:46:58,219 - src.core.analysis.advanced_fibonacci - INFO - 📏 Diferença Swing: 1,030.00 pontos
|
| 29 |
+
2025-08-27 09:46:58,244 - src.core.analysis.advanced_fibonacci - INFO - 📈 Níveis de Retracement calculados: 13 níveis
|
| 30 |
+
2025-08-27 09:46:58,297 - src.core.analysis.advanced_fibonacci - INFO - 📊 Níveis de Extensão calculados: 11 níveis
|
| 31 |
+
2025-08-27 09:46:58,323 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Níveis de Projeção calculados: 8 níveis
|
| 32 |
+
2025-08-27 09:46:58,325 - src.core.analysis.advanced_fibonacci - INFO - 🔢 Total de níveis Fibonacci: 32
|
| 33 |
+
2025-08-27 09:46:58,327 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Zonas de Confluência detectadas: 0
|
| 34 |
+
2025-08-27 09:46:58,329 - src.core.analysis.advanced_fibonacci - INFO - 🎼 Padrões Harmônicos detectados: 0
|
| 35 |
+
2025-08-27 09:46:58,332 - src.core.analysis.advanced_fibonacci - INFO - ⏰ Projeções Temporais calculadas: 0
|
| 36 |
+
2025-08-27 09:46:58,332 - src.core.analysis.advanced_fibonacci - INFO - 💪 Força Geral da Análise: 0.00
|
| 37 |
+
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
|
| 38 |
+
2025-08-27 09:46:58,339 - src.core.analysis.advanced_fibonacci - INFO - 🔮 INICIANDO ANÁLISE AVANÇADA DE FIBONACCI - Lookback: 40 períodos
|
| 39 |
+
2025-08-27 09:46:58,340 - src.core.analysis.advanced_fibonacci - INFO - 📊 Swing Points - Alta: 140,570.00, Baixa: 139,540.00, Atual: 140,135.00
|
| 40 |
+
2025-08-27 09:46:58,340 - src.core.analysis.advanced_fibonacci - INFO - 📏 Diferença Swing: 1,030.00 pontos
|
| 41 |
+
2025-08-27 09:46:58,365 - src.core.analysis.advanced_fibonacci - INFO - 📈 Níveis de Retracement calculados: 13 níveis
|
| 42 |
+
2025-08-27 09:46:58,396 - src.core.analysis.advanced_fibonacci - INFO - 📊 Níveis de Extensão calculados: 11 níveis
|
| 43 |
+
2025-08-27 09:46:58,418 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Níveis de Projeção calculados: 8 níveis
|
| 44 |
+
2025-08-27 09:46:58,419 - src.core.analysis.advanced_fibonacci - INFO - 🔢 Total de níveis Fibonacci: 32
|
| 45 |
+
2025-08-27 09:46:58,421 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Zonas de Confluência detectadas: 0
|
| 46 |
+
2025-08-27 09:46:58,423 - src.core.analysis.advanced_fibonacci - INFO - 🎼 Padrões Harmônicos detectados: 0
|
| 47 |
+
2025-08-27 09:46:58,424 - src.core.analysis.advanced_fibonacci - INFO - ⏰ Projeções Temporais calculadas: 0
|
| 48 |
+
2025-08-27 09:46:58,425 - src.core.analysis.advanced_fibonacci - INFO - 💪 Força Geral da Análise: 0.00
|
| 49 |
+
2025-08-27 09:46:58,427 - src.core.analysis.advanced_fibonacci - INFO - 🔮 ANÁLISE CONCLUÍDA - Zona: ZONA_MEDIA_ALTA, Suporte: 140133.28, Resistência: 140176.54
|
| 50 |
+
2025-08-27 09:46:58,428 - src.core.analysis.advanced_fibonacci - INFO - 🔮 INICIANDO ANÁLISE AVANÇADA DE FIBONACCI - Lookback: 40 períodos
|
| 51 |
+
2025-08-27 09:46:58,430 - src.core.analysis.advanced_fibonacci - INFO - 📊 Swing Points - Alta: 140,570.00, Baixa: 139,540.00, Atual: 140,135.00
|
| 52 |
+
2025-08-27 09:46:58,431 - src.core.analysis.advanced_fibonacci - INFO - 📏 Diferença Swing: 1,030.00 pontos
|
| 53 |
+
2025-08-27 09:46:58,460 - src.core.analysis.advanced_fibonacci - INFO - 📈 Níveis de Retracement calculados: 13 níveis
|
| 54 |
+
2025-08-27 09:46:58,487 - src.core.analysis.advanced_fibonacci - INFO - 📊 Níveis de Extensão calculados: 11 níveis
|
| 55 |
+
2025-08-27 09:46:58,504 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Níveis de Projeção calculados: 8 níveis
|
| 56 |
+
2025-08-27 09:46:58,520 - src.core.analysis.advanced_fibonacci - INFO - 🔢 Total de níveis Fibonacci: 32
|
| 57 |
+
2025-08-27 09:46:58,543 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Zonas de Confluência detectadas: 0
|
| 58 |
+
2025-08-27 09:46:58,558 - src.core.analysis.advanced_fibonacci - INFO - 🎼 Padrões Harmônicos detectados: 0
|
| 59 |
+
2025-08-27 09:46:58,575 - src.core.analysis.advanced_fibonacci - INFO - ⏰ Projeções Temporais calculadas: 0
|
| 60 |
+
2025-08-27 09:46:58,606 - src.core.analysis.advanced_fibonacci - INFO - 💪 Força Geral da Análise: 0.00
|
| 61 |
+
2025-08-27 09:46:58,614 - src.core.analysis.advanced_fibonacci - INFO - 🔮 ANÁLISE CONCLUÍDA - Zona: ZONA_MEDIA_ALTA, Suporte: 140133.28, Resistência: 140176.54
|
| 62 |
+
2025-08-27 09:46:58,618 - src.core.analysis.advanced_fibonacci - INFO - 🔮 INICIANDO ANÁLISE AVANÇADA DE FIBONACCI - Lookback: 40 períodos
|
| 63 |
+
2025-08-27 09:46:58,622 - src.core.analysis.advanced_fibonacci - INFO - 📊 Swing Points - Alta: 140,570.00, Baixa: 139,540.00, Atual: 140,135.00
|
| 64 |
+
2025-08-27 09:46:58,624 - src.core.analysis.advanced_fibonacci - INFO - 📏 Diferença Swing: 1,030.00 pontos
|
| 65 |
+
2025-08-27 09:46:58,696 - src.core.analysis.advanced_fibonacci - INFO - 📈 Níveis de Retracement calculados: 13 níveis
|
| 66 |
+
2025-08-27 09:46:58,758 - src.core.analysis.advanced_fibonacci - INFO - 📊 Níveis de Extensão calculados: 11 níveis
|
| 67 |
+
2025-08-27 09:46:58,808 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Níveis de Projeção calculados: 8 níveis
|
| 68 |
+
2025-08-27 09:46:58,821 - src.core.analysis.advanced_fibonacci - INFO - 🔢 Total de níveis Fibonacci: 32
|
| 69 |
+
2025-08-27 09:46:58,832 - src.core.analysis.advanced_fibonacci - INFO - 🎯 Zonas de Confluência detectadas: 0
|
| 70 |
+
2025-08-27 09:46:58,840 - src.core.analysis.advanced_fibonacci - INFO - 🎼 Padrões Harmônicos detectados: 0
|
| 71 |
+
2025-08-27 09:46:58,841 - src.core.analysis.advanced_fibonacci - INFO - ⏰ Projeções Temporais calculadas: 0
|
| 72 |
+
2025-08-27 09:46:58,850 - src.core.analysis.advanced_fibonacci - INFO - 💪 Força Geral da Análise: 0.00
|
| 73 |
+
2025-08-27 09:46:58,855 - src.core.analysis.advanced_fibonacci - INFO - 🔮 ANÁLISE CONCLUÍDA - Zona: ZONA_MEDIA_ALTA, Suporte: 140133.28, Resistência: 140176.54
|
| 74 |
+
2025-08-27 09:46:58,859 - src.core.managers.performance_optimizer - WARNING - Análise de mercado lenta: 0.526s
|
| 75 |
+
2025-08-27 09:46:58,869 - __main__ - INFO - 📊 FIBONACCI AVANÇADO ENVIADO: Alertas:15 FibSinal:HOLD
|
| 76 |
+
🔮 Fibonacci Avançado: Alertas:15 FibSinal:HOLD
|
ui.py
CHANGED
|
@@ -17,6 +17,194 @@ from utils import (
|
|
| 17 |
class UIComponents:
|
| 18 |
"""Componentes da interface do usuário."""
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
@staticmethod
|
| 21 |
def create_header() -> str:
|
| 22 |
"""Cria cabeçalho da aplicação."""
|
|
@@ -44,20 +232,47 @@ class UIComponents:
|
|
| 44 |
</div>
|
| 45 |
""")
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
-
return input_section, market_input, analyze_btn
|
| 61 |
|
| 62 |
@staticmethod
|
| 63 |
def create_output_section() -> Tuple[gr.Column, Dict[str, Any]]:
|
|
@@ -87,11 +302,25 @@ class UIComponents:
|
|
| 87 |
with gr.Tab("💡 Recomendações"):
|
| 88 |
outputs['recommendations'] = gr.HTML()
|
| 89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
# Aba de Dados Brutos
|
| 91 |
with gr.Tab("🔍 Dados Detalhados"):
|
| 92 |
outputs['raw_data'] = gr.JSON()
|
| 93 |
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
@staticmethod
|
| 97 |
def create_footer(model_info: Optional[Dict[str, Any]] = None) -> str:
|
|
@@ -329,6 +558,61 @@ class GradioInterface:
|
|
| 329 |
self.model_info = model_info or {'available': False}
|
| 330 |
self.interface = None
|
| 331 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
def create_interface(self) -> gr.Blocks:
|
| 333 |
"""Cria interface completa do Gradio."""
|
| 334 |
with gr.Blocks(
|
|
@@ -343,7 +627,7 @@ class GradioInterface:
|
|
| 343 |
with gr.Row():
|
| 344 |
# Coluna de entrada (40%)
|
| 345 |
with gr.Column(scale=2):
|
| 346 |
-
input_section, market_input, analyze_btn = UIComponents.create_input_section()
|
| 347 |
|
| 348 |
# Coluna de saída (60%)
|
| 349 |
with gr.Column(scale=3):
|
|
@@ -352,7 +636,7 @@ class GradioInterface:
|
|
| 352 |
# Rodapé
|
| 353 |
gr.HTML(UIComponents.create_footer(self.model_info))
|
| 354 |
|
| 355 |
-
# Configurar
|
| 356 |
analyze_btn.click(
|
| 357 |
fn=self._analyze_wrapper,
|
| 358 |
inputs=[market_input],
|
|
@@ -362,6 +646,23 @@ class GradioInterface:
|
|
| 362 |
outputs['technical_analysis'],
|
| 363 |
outputs['sentiment_analysis'],
|
| 364 |
outputs['recommendations'],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
outputs['raw_data']
|
| 366 |
]
|
| 367 |
)
|
|
@@ -378,7 +679,7 @@ class GradioInterface:
|
|
| 378 |
self.interface = interface
|
| 379 |
return interface
|
| 380 |
|
| 381 |
-
def _analyze_wrapper(self, market_input: str) -> Tuple[str, str, str, str, str, Dict[str, Any]]:
|
| 382 |
"""Wrapper para função de análise com formatação de saída."""
|
| 383 |
try:
|
| 384 |
# Executar análise
|
|
@@ -394,6 +695,8 @@ class GradioInterface:
|
|
| 394 |
technical_analysis = ResultFormatter.format_technical_analysis(analysis_result)
|
| 395 |
sentiment_analysis = ResultFormatter.format_sentiment_analysis(analysis_result)
|
| 396 |
recommendations = ResultFormatter.format_recommendations(analysis_result)
|
|
|
|
|
|
|
| 397 |
|
| 398 |
# Dados brutos para debug
|
| 399 |
raw_data = {
|
|
@@ -407,6 +710,8 @@ class GradioInterface:
|
|
| 407 |
technical_analysis,
|
| 408 |
sentiment_analysis,
|
| 409 |
recommendations,
|
|
|
|
|
|
|
| 410 |
raw_data
|
| 411 |
)
|
| 412 |
|
|
@@ -425,6 +730,43 @@ class GradioInterface:
|
|
| 425 |
error_html,
|
| 426 |
error_html,
|
| 427 |
error_html,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 428 |
{'error': error_msg}
|
| 429 |
)
|
| 430 |
|
|
|
|
| 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."""
|
|
|
|
| 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]]:
|
|
|
|
| 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:
|
|
|
|
| 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(
|
|
|
|
| 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):
|
|
|
|
| 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],
|
|
|
|
| 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 |
)
|
|
|
|
| 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
|
|
|
|
| 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 = {
|
|
|
|
| 710 |
technical_analysis,
|
| 711 |
sentiment_analysis,
|
| 712 |
recommendations,
|
| 713 |
+
harmonic_patterns,
|
| 714 |
+
fibonacci_alerts,
|
| 715 |
raw_data
|
| 716 |
)
|
| 717 |
|
|
|
|
| 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 |
|