index.html•28.4 kB
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Trading Chatbot - IA Conversacional</title>
<!-- Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Custom CSS -->
<style>
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* Header */
.header {
text-align: center;
margin-bottom: 30px;
color: white;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
}
/* Status indicators */
.status-bar {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 20px;
}
.mode-toggle {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 16px;
background: rgba(255, 255, 255, 0.1);
border-radius: 20px;
color: white;
font-size: 0.9rem;
}
.mode-toggle label {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.mode-toggle input[type="checkbox"] {
display: none;
}
.mode-slider {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
background-color: #ccc;
border-radius: 24px;
transition: .4s;
}
.mode-slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border-radius: 50%;
}
input:checked + .mode-slider {
background-color: #2196F3;
}
input:checked + .mode-slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.mode-text {
font-weight: bold;
}
.mode-hint {
transition: all 0.3s ease;
}
.status-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
background: rgba(255, 255, 255, 0.1);
border-radius: 20px;
color: white;
font-size: 0.9rem;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #ff6b6b;
}
.status-dot.healthy {
background: #51cf66;
}
.status-dot.warning {
background: #ffd43b;
}
/* Main chat interface */
.chat-container {
display: flex;
height: calc(100vh - 200px);
min-height: 600px;
background: white;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
}
.sidebar {
width: 300px;
background: #f8f9fa;
border-right: 1px solid #e9ecef;
padding: 20px;
display: flex;
flex-direction: column;
}
.sidebar h3 {
color: #495057;
margin-bottom: 20px;
font-size: 1.1rem;
}
.sidebar-section {
margin-bottom: 30px;
}
.quick-commands {
display: grid;
gap: 10px;
}
.quick-btn {
width: 100%;
padding: 12px;
background: #667eea;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.9rem;
text-align: left;
}
.quick-btn:hover {
background: #5a67d8;
transform: translateY(-2px);
}
.quick-btn:active {
transform: translateY(0);
}
.symbol-list {
max-height: 300px;
overflow-y: auto;
}
.symbol-item {
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
margin-bottom: 4px;
transition: background 0.2s;
}
.symbol-item:hover {
background: #e9ecef;
}
/* Chat area */
.chat-area {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
background: #fafbfc;
}
.message {
margin-bottom: 20px;
animation: fadeIn 0.3s ease-in;
}
.message.user {
text-align: right;
}
.message-content {
display: inline-block;
max-width: 70%;
padding: 12px 18px;
border-radius: 18px;
font-size: 0.95rem;
line-height: 1.4;
}
.message.user .message-content {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
margin-left: auto;
}
.message.bot .message-content {
background: white;
color: #495057;
border: 1px solid #e9ecef;
}
.message.bot .message-content.confirmed {
border-left: 4px solid #51cf66;
}
.message.bot .message-content.error {
border-left: 4px solid #ff6b6b;
}
.message.bot .message-content.warning {
border-left: 4px solid #ffd43b;
}
.confirmation-buttons {
margin-top: 10px;
text-align: right;
}
.confirm-btn {
padding: 6px 12px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 0.8rem;
margin-left: 8px;
}
.confirm-btn.confirm {
background: #51cf66;
color: white;
}
.confirm-btn.cancel {
background: #ff6b6b;
color: white;
}
.intent-info {
font-size: 0.8rem;
opacity: 0.7;
margin-top: 4px;
}
/* Input area */
.input-area {
padding: 20px;
background: white;
border-top: 1px solid #e9ecef;
display: flex;
gap: 10px;
align-items: flex-end;
}
.message-input {
flex: 1;
min-height: 50px;
max-height: 150px;
padding: 12px 16px;
border: 2px solid #e9ecef;
border-radius: 25px;
resize: none;
font-family: inherit;
font-size: 1rem;
outline: none;
transition: border-color 0.3s;
}
.message-input:focus {
border-color: #667eea;
}
.send-btn {
width: 50px;
height: 50px;
border: none;
border-radius: 50%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.send-btn:hover {
transform: scale(1.05);
}
.send-btn:active {
transform: scale(0.95);
}
.send-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Loading animation */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid #f3f3f3;
border-top: 2px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
}
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Typing indicator */
.typing {
display: none;
font-style: italic;
color: #6c757d;
padding: 10px 0;
}
/* Responsive design */
@media (max-width: 768px) {
.chat-container {
flex-direction: column;
height: calc(100vh - 160px);
}
.sidebar {
width: 100%;
height: 200px;
border-right: none;
border-bottom: 1px solid #e9ecef;
}
.header h1 {
font-size: 2rem;
}
.status-bar {
flex-wrap: wrap;
}
}
/* Dark mode support */
@media (prefers-color-scheme: dark) {
body {
background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%);
color: #e2e8f0;
}
.chat-messages {
background: #2d3748;
}
.message.bot .message-content {
background: #4a5568;
color: #e2e8f0;
border-color: #718096;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1><i class="fas fa-robot"></i> Trading Chatbot</h1>
<p>IA Conversacional para Operações de Trading Automatizadas</p>
</div>
<!-- Status Bar -->
<div class="status-bar">
<div class="status-item" id="ollama-status">
<div class="status-dot" id="ollama-dot"></div>
<span>Ollama</span>
</div>
<div class="status-item" id="mt5-status">
<div class="status-dot" id="mt5-dot"></div>
<span>MetaTrader 5</span>
</div>
<div class="status-item" id="chatbot-status">
<div class="status-dot" id="chatbot-dot"></div>
<span>Chatbot</span>
</div>
<!-- MODE TOGGLE -->
<div class="mode-toggle">
<span class="mode-text" id="mode-text">🤖 Modo Direto MT5</span>
<label class="toggle-label">
<input type="checkbox" id="mode-toggle" onchange="toggleAIMode(event)">
<span class="mode-slider"></span>
</label>
<span class="mode-hint" id="mode-hint">Use 'ok' para confirmar</span>
</div>
</div>
<!-- Chat Interface -->
<div class="chat-container">
<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-section">
<h3><i class="fas fa-lightning-bolt"></i> Comandos Rápidos</h3>
<div class="quick-commands">
<button class="quick-btn" onclick="sendQuickMessage('Quanto tenho de saldo?')">
<i class="fas fa-dollar-sign"></i> Ver Saldo
</button>
<button class="quick-btn" onclick="sendQuickMessage('Mostrar minhas posições abertas')">
<i class="fas fa-chart-line"></i> Ver Posições
</button>
<button class="quick-btn" onclick="sendQuickMessage('Qual é a melhor oportunidade de trading agora?')">
<i class="fas fa-search"></i> Análise de Mercado
</button>
<button class="quick-btn" onclick="sendQuickMessage('Comprar EURUSD 0.01 lots')">
<i class="fas fa-shopping-cart"></i> Compra EURUSD
</button>
<button class="quick-btn" onclick="sendQuickMessage('Vender BTCUSD 0.001 lots')">
<i class="fas fa-sell"></i> Venda BTCUSD
</button>
</div>
</div>
<div class="sidebar-section">
<h3><i class="fas fa-coins"></i> Símbolos Populares</h3>
<div class="symbol-list" id="symbols-list">
<!-- Populated by JavaScript -->
</div>
</div>
</div>
<!-- Chat Area -->
<div class="chat-area">
<!-- Messages Container -->
<div class="chat-messages" id="messages">
<div class="message bot">
<div class="message-content">
<i class="fas fa-robot"></i> <strong>Trading Bot:</strong><br><br>
Olá! Sou seu assistente de trading inteligente. Posso ajudar você com:<br><br>
• <strong>Ordens de compra/venda:</strong> "Comprar 100 EURUSD" ou "Vender 0.01 BTCUSD com stop loss"<br>
• <strong>Informações da conta:</strong> "Quanto tenho de saldo?" ou "Mostrar posições"<br>
• <strong>Análises:</strong> "Analisar EURUSD" ou "Melhores oportunidades agora"<br>
• <strong>Ordens pendentes:</strong> "Mostrar ordens ativas" ou "Cancelar ordem 123"<br><br>
🛡️ <strong>Segurança:</strong> Sempre confirmo operações de alto risco antes de executar.
</div>
</div>
</div>
<!-- Typing Indicator -->
<div class="typing" id="typing-indicator">
<i class="fas fa-circle"></i>
<i class="fas fa-circle"></i>
<i class="fas fa-circle"></i>
O chatbot está digitando...
</div>
<!-- Input Area -->
<div class="input-area">
<textarea
id="message-input"
class="message-input"
placeholder="Digite sua mensagem aqui... (ex: 'Comprar EURUSD 0.01 lots')"
rows="1"
onkeydown="handleKeyDown(event)"
oninput="autoResizeTextarea()"
></textarea>
<button id="send-btn" class="send-btn" onclick="sendMessage()">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
</div>
<!-- JavaScript -->
<script>
// Global variables
let isInitialized = false;
let isTyping = false;
let aiMode = false; // Start in MT5 direct mode
// DOM elements
const messagesContainer = document.getElementById('messages');
const messageInput = document.getElementById('message-input');
const sendBtn = document.getElementById('send-btn');
const typingIndicator = document.getElementById('typing-indicator');
const modeText = document.getElementById('mode-text');
const modeHint = document.getElementById('mode-hint');
const modeToggle = document.getElementById('mode-toggle');
// Initialize when page loads
window.onload = async () => {
updateStatusDots();
await initializeChatbot();
loadSymbols();
messageInput.focus();
};
// Update status dots based on current status
async function updateStatusDots() {
try {
const response = await fetch('/api/chatbot/status');
const status = await response.json();
// Accept 'available' or 'healthy' as green status
const ollamaHealthy = status.ollama?.status === 'healthy' || status.ollama?.status === 'available';
const mt5Healthy = status.mt5?.status === 'connected' || status.mt5?.status === 'healthy';
updateDot('ollama', ollamaHealthy ? 'healthy' : 'error');
updateDot('mt5', mt5Healthy ? 'healthy' : 'error');
updateDot('chatbot', status.status === 'initialized' ? 'healthy' :
status.status === 'not_initialized' ? 'warning' : 'error');
} catch (error) {
console.error('Failed to update status:', error);
}
}
function updateDot(service, status) {
const dot = document.getElementById(`${service}-dot`);
if (dot) {
dot.className = `status-dot ${status}`;
}
}
// Initialize chatbot
async function initializeChatbot() {
try {
setTyping(true);
const response = await fetch('/api/chatbot/initialize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const result = await response.json();
if (result.success) {
isInitialized = true;
addMessage('Sistema inicializado com sucesso!', 'info');
} else {
addMessage(`Erro na inicialização: ${result.message}`, 'error');
}
} catch (error) {
console.error('Initialization failed:', error);
addMessage('Falha ao inicializar o chatbot', 'error');
} finally {
setTyping(false);
await updateStatusDots();
}
}
// Load trading symbols
async function loadSymbols() {
try {
const response = await fetch('/api/trading/symbols');
const result = await response.json();
if (result.success) {
const symbolsList = document.getElementById('symbols-list');
symbolsList.innerHTML = '';
result.symbols.forEach(symbol => {
const symbolDiv = document.createElement('div');
symbolDiv.className = 'symbol-item';
symbolDiv.textContent = symbol;
symbolDiv.onclick = () => sendQuickMessage(`Mostrar preço de ${symbol}`);
symbolsList.appendChild(symbolDiv);
});
}
} catch (error) {
console.error('Failed to load symbols:', error);
}
}
// Send message
async function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;
if (!isInitialized) {
addMessage('Aguarde a inicialização do chatbot...', 'warning');
return;
}
// Add user message to UI
addMessage(message, 'user');
messageInput.value = '';
autoResizeTextarea();
// Disable input while processing
setInputEnabled(false);
try {
setTyping(true);
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: message,
use_ollama: aiMode // Include AI mode flag
})
});
const result = await response.json();
if (result.success) {
const intent = result.intent;
const botResponse = result.response;
const statusClass = getStatusClass(result.result);
addMessage(botResponse, 'bot', statusClass);
// Handle confirmations
if (result.result && result.result.status === 'confirmation_required') {
addConfirmationButtons(result.result);
}
} else {
addMessage(`Erro: ${result.message}`, 'error');
}
} catch (error) {
console.error('Failed to send message:', error);
addMessage('Falha ao enviar mensagem', 'error');
} finally {
setTyping(false);
setInputEnabled(true);
messageInput.focus();
}
}
// Send quick message
function sendQuickMessage(message) {
messageInput.value = message;
autoResizeTextarea();
sendMessage();
}
// Add message to chat
function addMessage(content, type = 'bot', statusClass = '') {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
const contentDiv = document.createElement('div');
contentDiv.className = `message-content ${statusClass}`;
contentDiv.innerHTML = type === 'user' ? content : formatBotMessage(content);
messageDiv.appendChild(contentDiv);
messagesContainer.appendChild(messageDiv);
scrollToBottom();
return messageDiv;
}
// Format bot message with HTML
function formatBotMessage(content) {
return content
.replace(/\n/g, '<br>')
.replace(/\• /g, '• ')
.replace(/\🛡️ /g, '<i class="fas fa-shield-alt"></i> ')
.replace(/\📊 /g, '<i class="fas fa-chart-bar"></i> ')
.replace(/\💰 /g, '<i class="fas fa-dollar-sign"></i> ')
.replace(/(BUY|SELL|LONG|SHORT)/gi, '<strong>$1</strong>');
}
// Get status class for message styling
function getStatusClass(result) {
if (!result || !result.status) return '';
switch (result.status) {
case 'success': return 'confirmed';
case 'error': return 'error';
case 'confirmation_required': return 'warning';
default: return '';
}
}
// Add confirmation buttons for high-risk trades
function addConfirmationButtons(params) {
const lastMessage = messagesContainer.lastElementChild;
if (!lastMessage || !lastMessage.classList.contains('bot')) return;
const buttonsDiv = document.createElement('div');
buttonsDiv.className = 'confirmation-buttons';
const confirmBtn = document.createElement('button');
confirmBtn.className = 'confirm-btn confirm';
confirmBtn.textContent = 'Confirmar';
confirmBtn.onclick = () => confirmTrade(params.command, params.params);
const cancelBtn = document.createElement('button');
cancelBtn.className = 'confirm-btn cancel';
cancelBtn.textContent = 'Cancelar';
buttonsDiv.appendChild(confirmBtn);
buttonsDiv.appendChild(cancelBtn);
lastMessage.appendChild(buttonsDiv);
}
// Confirm trade execution
async function confirmTrade(command, params) {
try {
setTyping(true);
const response = await fetch('/api/chat/confirm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ command: command, params: params })
});
const result = await response.json();
if (result.success) {
addMessage(`✅ ${result.message}`, 'confirmed');
} else {
addMessage(`❌ Erro: ${result.message}`, 'error');
}
} catch (error) {
console.error('Failed to confirm trade:', error);
addMessage('Falha ao confirmar operação', 'error');
} finally {
setTyping(false);
}
}
// Set typing indicator
function setTyping(typing) {
isTyping = typing;
typingIndicator.style.display = typing ? 'block' : 'none';
if (typing) scrollToBottom();
}
// Enable/disable input
function setInputEnabled(enabled) {
messageInput.disabled = !enabled;
sendBtn.disabled = !enabled;
}
// Handle keyboard shortcuts
function handleKeyDown(event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
sendMessage();
}
}
// Auto-resize textarea
function autoResizeTextarea() {
messageInput.style.height = 'auto';
messageInput.style.height = Math.min(messageInput.scrollHeight, 150) + 'px';
}
// Scroll to bottom of messages
function scrollToBottom() {
setTimeout(() => {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}, 100);
}
// Toggle AI mode
function toggleAIMode(event) {
const isChecked = event.target.checked;
aiMode = isChecked;
if (isChecked) {
modeText.textContent = '🧠 Modo IA Inteligente';
modeHint.textContent = 'IA pensa e analisa antes agir';
messageInput.placeholder = 'Pergunte à IA inteligente... (ex: "O que você recomenda hoje?")';
addMessage('🤖 <strong>MODO IA INTELIGENTE ATIVADO</strong><br><br>Agora a IA irá analisar suas solicitações, pensar sobre estratégias, avaliar riscos e executar operações com raciocínio avançado!<br><br>🔄 Funcionalidades IA:<br>• Análise inteligente de intenções<br>• Avaliação de risco automático<br>• Análise de mercado<br>• Execução automática ou confirmação', 'system');
} else {
modeText.textContent = '🤖 Modo Direto MT5';
modeHint.textContent = 'Use \'ok\' para confirmar';
messageInput.placeholder = 'Digite sua mensagem aqui... (ex: \'Comprar EURUSD 0.01 lots\')';
addMessage('⚡ <strong>MODO DIRETO MT5 ATIVADO</strong><br><br>Agora você conversa diretamente com o MetaTrader 5 através de comandos simples em português.', 'system');
}
// Save preference
localStorage.setItem('tradingChatbotAIMode', aiMode);
}
// Load saved mode preference
if (localStorage.getItem('tradingChatbotAIMode') === 'true') {
modeToggle.checked = true;
aiMode = true;
modeText.textContent = '🧠 Modo IA Inteligente';
modeHint.textContent = 'IA pensa e analisa antes agir';
messageInput.placeholder = 'Pergunte à IA inteligente... (ex: "O que você recomenda hoje?")';
}
// Update status periodically
setInterval(updateStatusDots, 30000); // Every 30 seconds
</script>
</body>
</html>