480 lines
No EOL
16 KiB
HTML
480 lines
No EOL
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="pt-BR">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Simulador HFT WINV25</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 20px;
|
|
background-color: #1a1a1a;
|
|
color: #ffffff;
|
|
}
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
.panel {
|
|
background-color: #2d2d2d;
|
|
border: 1px solid #444;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
margin: 10px 0;
|
|
}
|
|
.controls {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.control-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
label {
|
|
color: #aaa;
|
|
font-size: 12px;
|
|
margin-bottom: 5px;
|
|
}
|
|
input {
|
|
background-color: #3a3a3a;
|
|
border: 1px solid #555;
|
|
color: #fff;
|
|
padding: 8px;
|
|
border-radius: 4px;
|
|
}
|
|
button {
|
|
background-color: #0066cc;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
margin: 5px;
|
|
}
|
|
button:hover {
|
|
background-color: #0088ff;
|
|
}
|
|
button.stop {
|
|
background-color: #cc0000;
|
|
}
|
|
button.stop:hover {
|
|
background-color: #ff0000;
|
|
}
|
|
.chart-area {
|
|
background-color: #1a1a1a;
|
|
border: 1px solid #444;
|
|
height: 300px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #666;
|
|
margin: 20px 0;
|
|
}
|
|
.logs {
|
|
background-color: #0a0a0a;
|
|
border: 1px solid #333;
|
|
height: 200px;
|
|
overflow-y: auto;
|
|
padding: 10px;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
}
|
|
.position {
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin: 5px 0;
|
|
}
|
|
.position.long {
|
|
background-color: #1a4a1a;
|
|
border: 1px solid #2a7a2a;
|
|
}
|
|
.position.short {
|
|
background-color: #4a1a1a;
|
|
border: 1px solid #7a2a2a;
|
|
}
|
|
.position.neutral {
|
|
background-color: #2a2a2a;
|
|
border: 1px solid #4a4a4a;
|
|
}
|
|
.metrics {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: 10px;
|
|
}
|
|
.metric {
|
|
text-align: center;
|
|
padding: 10px;
|
|
background-color: #333;
|
|
border-radius: 4px;
|
|
}
|
|
.metric-value {
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #00ff88;
|
|
}
|
|
.metric-label {
|
|
font-size: 12px;
|
|
color: #aaa;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🤖 Simulador HFT WINV25</h1>
|
|
|
|
<div class="panel">
|
|
<h3>⚙️ Configurações do Robô</h3>
|
|
<div class="controls">
|
|
<div class="control-group">
|
|
<label>Take Profit (pontos)</label>
|
|
<input type="number" id="takeProfit" value="15">
|
|
</div>
|
|
<div class="control-group">
|
|
<label>Stop Loss (pontos)</label>
|
|
<input type="number" id="stopLoss" value="10">
|
|
</div>
|
|
<div class="control-group">
|
|
<label>Volume Mínimo</label>
|
|
<input type="number" id="volumeMin" value="100">
|
|
</div>
|
|
<div class="control-group">
|
|
<label>Preço Atual</label>
|
|
<input type="number" id="currentPrice" value="130000" step="1">
|
|
</div>
|
|
<div class="control-group">
|
|
<label>Volume Atual</label>
|
|
<input type="number" id="currentVolume" value="150">
|
|
</div>
|
|
<div class="control-group">
|
|
<label>Hora Atual (HHMM)</label>
|
|
<input type="text" id="currentTime" value="1030">
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<button onclick="startSimulation()">▶️ Iniciar Simulação</button>
|
|
<button onclick="stopSimulation()" class="stop">⏹️ Parar</button>
|
|
<button onclick="executeManualTrade('BUY')">📈 Compra Manual</button>
|
|
<button onclick="executeManualTrade('SELL')">📉 Venda Manual</button>
|
|
<button onclick="closePosition()">❌ Fechar Posição</button>
|
|
<button onclick="clearLogs()">🗑️ Limpar Logs</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h3>📊 Status da Posição</h3>
|
|
<div id="positionStatus" class="position neutral">
|
|
<strong>Sem Posição</strong><br>
|
|
Aguardando sinal de entrada...
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h3>📈 Métricas de Performance</h3>
|
|
<div class="metrics">
|
|
<div class="metric">
|
|
<div class="metric-value" id="totalTrades">0</div>
|
|
<div class="metric-label">Total Operações</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value" id="winRate">0%</div>
|
|
<div class="metric-label">Taxa de Acerto</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value" id="totalPL">R$ 0</div>
|
|
<div class="metric-label">P&L Total</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value" id="currentPL">R$ 0</div>
|
|
<div class="metric-label">P&L Atual</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<h3>📝 Log de Operações</h3>
|
|
<div id="logs" class="logs">
|
|
<div style="color: #888;">Simulador iniciado. Configure os parâmetros e clique em "Iniciar Simulação"</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Variáveis do simulador
|
|
let isRunning = false;
|
|
let position = 0; // 0 = sem posição, >0 = comprado, <0 = vendido
|
|
let entryPrice = 0;
|
|
let trades = [];
|
|
let simulationInterval;
|
|
|
|
// Métricas
|
|
let totalTrades = 0;
|
|
let winningTrades = 0;
|
|
let totalPL = 0;
|
|
|
|
function log(message) {
|
|
const logs = document.getElementById('logs');
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
logs.innerHTML += `<div>[${timestamp}] ${message}</div>`;
|
|
logs.scrollTop = logs.scrollHeight;
|
|
}
|
|
|
|
function updateMetrics() {
|
|
document.getElementById('totalTrades').textContent = totalTrades;
|
|
document.getElementById('winRate').textContent = totalTrades > 0 ?
|
|
Math.round((winningTrades / totalTrades) * 100) + '%' : '0%';
|
|
document.getElementById('totalPL').textContent = `R$ ${totalPL.toFixed(2)}`;
|
|
|
|
// P&L atual da posição
|
|
const currentPrice = parseFloat(document.getElementById('currentPrice').value);
|
|
let currentPL = 0;
|
|
if (position > 0) {
|
|
currentPL = (currentPrice - entryPrice) * 0.20; // R$ 0,20 por ponto
|
|
} else if (position < 0) {
|
|
currentPL = (entryPrice - currentPrice) * 0.20;
|
|
}
|
|
document.getElementById('currentPL').textContent = `R$ ${currentPL.toFixed(2)}`;
|
|
}
|
|
|
|
function updatePositionStatus() {
|
|
const statusDiv = document.getElementById('positionStatus');
|
|
const currentPrice = parseFloat(document.getElementById('currentPrice').value);
|
|
|
|
if (position === 0) {
|
|
statusDiv.className = 'position neutral';
|
|
statusDiv.innerHTML = '<strong>Sem Posição</strong><br>Aguardando sinal de entrada...';
|
|
} else if (position > 0) {
|
|
const pl = (currentPrice - entryPrice) * 0.20;
|
|
statusDiv.className = 'position long';
|
|
statusDiv.innerHTML = `
|
|
<strong>COMPRADO</strong><br>
|
|
Entrada: ${entryPrice.toFixed(0)} pontos<br>
|
|
Atual: ${currentPrice.toFixed(0)} pontos<br>
|
|
P&L: R$ ${pl.toFixed(2)}
|
|
`;
|
|
} else {
|
|
const pl = (entryPrice - currentPrice) * 0.20;
|
|
statusDiv.className = 'position short';
|
|
statusDiv.innerHTML = `
|
|
<strong>VENDIDO</strong><br>
|
|
Entrada: ${entryPrice.toFixed(0)} pontos<br>
|
|
Atual: ${currentPrice.toFixed(0)} pontos<br>
|
|
P&L: R$ ${pl.toFixed(2)}
|
|
`;
|
|
}
|
|
}
|
|
|
|
function checkHorarioOperacao() {
|
|
const timeStr = document.getElementById('currentTime').value;
|
|
const hour = parseInt(timeStr.substring(0, 2));
|
|
const minute = parseInt(timeStr.substring(2, 4));
|
|
|
|
// Horário de operação: 08:55 às 17:45
|
|
if (hour < 8 || hour > 17) return false;
|
|
if (hour === 8 && minute < 55) return false;
|
|
if (hour === 17 && minute > 45) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
function checkEntrySignal() {
|
|
if (!checkHorarioOperacao()) return null;
|
|
if (position !== 0) return null;
|
|
|
|
const currentPrice = parseFloat(document.getElementById('currentPrice').value);
|
|
const volume = parseFloat(document.getElementById('currentVolume').value);
|
|
const minVolume = parseFloat(document.getElementById('volumeMin').value);
|
|
|
|
if (volume < minVolume) return null;
|
|
|
|
// Simular condições de entrada (simplificado)
|
|
const randomFactor = Math.random();
|
|
|
|
if (randomFactor > 0.7) {
|
|
return 'BUY';
|
|
} else if (randomFactor < 0.3) {
|
|
return 'SELL';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function executeManualTrade(signal) {
|
|
if (position !== 0) {
|
|
log('❌ Já existe uma posição aberta');
|
|
return;
|
|
}
|
|
|
|
if (!checkHorarioOperacao()) {
|
|
log('❌ Fora do horário de operação');
|
|
return;
|
|
}
|
|
|
|
const currentPrice = parseFloat(document.getElementById('currentPrice').value);
|
|
|
|
if (signal === 'BUY') {
|
|
position = 1;
|
|
entryPrice = currentPrice;
|
|
log(`📈 COMPRA executada em ${entryPrice.toFixed(0)} pontos`);
|
|
} else {
|
|
position = -1;
|
|
entryPrice = currentPrice;
|
|
log(`📉 VENDA executada em ${entryPrice.toFixed(0)} pontos`);
|
|
}
|
|
|
|
updatePositionStatus();
|
|
}
|
|
|
|
function checkExitSignal() {
|
|
if (position === 0) return false;
|
|
|
|
const currentPrice = parseFloat(document.getElementById('currentPrice').value);
|
|
const takeProfit = parseFloat(document.getElementById('takeProfit').value);
|
|
const stopLoss = parseFloat(document.getElementById('stopLoss').value);
|
|
|
|
if (position > 0) {
|
|
// Posição comprada
|
|
if (currentPrice >= entryPrice + takeProfit) {
|
|
executeExit('Take Profit');
|
|
return true;
|
|
} else if (currentPrice <= entryPrice - stopLoss) {
|
|
executeExit('Stop Loss');
|
|
return true;
|
|
}
|
|
} else {
|
|
// Posição vendida
|
|
if (currentPrice <= entryPrice - takeProfit) {
|
|
executeExit('Take Profit');
|
|
return true;
|
|
} else if (currentPrice >= entryPrice + stopLoss) {
|
|
executeExit('Stop Loss');
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function executeExit(reason) {
|
|
if (position === 0) return;
|
|
|
|
const currentPrice = parseFloat(document.getElementById('currentPrice').value);
|
|
let pl = 0;
|
|
|
|
if (position > 0) {
|
|
pl = (currentPrice - entryPrice) * 0.20;
|
|
log(`📉 Venda executada em ${currentPrice.toFixed(0)} pontos (${reason})`);
|
|
} else {
|
|
pl = (entryPrice - currentPrice) * 0.20;
|
|
log(`📈 Compra para cobrir executada em ${currentPrice.toFixed(0)} pontos (${reason})`);
|
|
}
|
|
|
|
totalTrades++;
|
|
if (pl > 0) winningTrades++;
|
|
totalPL += pl;
|
|
|
|
log(`💰 P&L: R$ ${pl.toFixed(2)} | Motivo: ${reason}`);
|
|
|
|
trades.push({
|
|
entry: entryPrice,
|
|
exit: currentPrice,
|
|
pl: pl,
|
|
reason: reason
|
|
});
|
|
|
|
position = 0;
|
|
entryPrice = 0;
|
|
|
|
updatePositionStatus();
|
|
updateMetrics();
|
|
}
|
|
|
|
function closePosition() {
|
|
if (position === 0) {
|
|
log('❌ Não há posição para fechar');
|
|
return;
|
|
}
|
|
|
|
executeExit('Fechamento Manual');
|
|
}
|
|
|
|
function simulateMarket() {
|
|
// Simular movimento de preço
|
|
const currentPrice = parseFloat(document.getElementById('currentPrice').value);
|
|
const variation = (Math.random() - 0.5) * 10; // Variação de -5 a +5 pontos
|
|
const newPrice = currentPrice + variation;
|
|
|
|
document.getElementById('currentPrice').value = newPrice.toFixed(0);
|
|
|
|
// Simular volume
|
|
const newVolume = Math.floor(Math.random() * 300) + 50;
|
|
document.getElementById('currentVolume').value = newVolume;
|
|
|
|
// Avançar tempo (simulação)
|
|
const timeStr = document.getElementById('currentTime').value;
|
|
let hour = parseInt(timeStr.substring(0, 2));
|
|
let minute = parseInt(timeStr.substring(2, 4));
|
|
|
|
minute += 1;
|
|
if (minute >= 60) {
|
|
minute = 0;
|
|
hour += 1;
|
|
}
|
|
|
|
const newTime = hour.toString().padStart(2, '0') + minute.toString().padStart(2, '0');
|
|
document.getElementById('currentTime').value = newTime;
|
|
}
|
|
|
|
function runSimulation() {
|
|
if (!isRunning) return;
|
|
|
|
// Simular mercado
|
|
simulateMarket();
|
|
|
|
// Verificar saída
|
|
if (checkExitSignal()) {
|
|
updatePositionStatus();
|
|
updateMetrics();
|
|
return;
|
|
}
|
|
|
|
// Verificar entrada
|
|
const signal = checkEntrySignal();
|
|
if (signal) {
|
|
executeManualTrade(signal);
|
|
}
|
|
|
|
updatePositionStatus();
|
|
updateMetrics();
|
|
}
|
|
|
|
function startSimulation() {
|
|
if (isRunning) return;
|
|
|
|
isRunning = true;
|
|
log('🚀 Simulação iniciada');
|
|
|
|
simulationInterval = setInterval(runSimulation, 1000); // 1 segundo
|
|
}
|
|
|
|
function stopSimulation() {
|
|
if (!isRunning) return;
|
|
|
|
isRunning = false;
|
|
clearInterval(simulationInterval);
|
|
log('⏹️ Simulação parada');
|
|
}
|
|
|
|
function clearLogs() {
|
|
document.getElementById('logs').innerHTML =
|
|
'<div style="color: #888;">Logs limpos. Simulador pronto.</div>';
|
|
}
|
|
|
|
// Inicialização
|
|
updatePositionStatus();
|
|
updateMetrics();
|
|
</script>
|
|
</body>
|
|
</html> |