Spaces:
Running
Running
<html lang="es"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Bitcoin Price Predictor</title> | |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/luxon@2.0.2"></script> | |
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.0.0"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> | |
<style> | |
* { | |
box-sizing: border-box; | |
margin: 0; | |
padding: 0; | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
} | |
body { | |
background-color: #f5f7fa; | |
color: #333; | |
line-height: 1.6; | |
} | |
.container { | |
width: 100%; | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 20px; | |
} | |
/* Header styles */ | |
header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 30px; | |
flex-wrap: wrap; | |
} | |
.header-title { | |
display: flex; | |
align-items: center; | |
font-size: 28px; | |
font-weight: bold; | |
color: #2d3748; | |
} | |
.header-title i { | |
color: #f7931a; | |
margin-right: 10px; | |
} | |
.header-controls { | |
display: flex; | |
align-items: center; | |
} | |
.refresh-btn { | |
background-color: #3182ce; | |
color: white; | |
border: none; | |
padding: 8px 16px; | |
border-radius: 6px; | |
cursor: pointer; | |
display: flex; | |
align-items: center; | |
margin-right: 15px; | |
transition: background-color 0.3s; | |
} | |
.refresh-btn:hover { | |
background-color: #2c5282; | |
} | |
.refresh-btn i { | |
margin-right: 5px; | |
} | |
.last-updated { | |
font-size: 14px; | |
color: #718096; | |
} | |
.header-subtitle { | |
width: 100%; | |
margin-top: 10px; | |
color: #4a5568; | |
font-size: 14px; | |
} | |
/* Card grid */ | |
.card-grid { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 20px; | |
margin-bottom: 30px; | |
} | |
.card { | |
flex: 1; | |
min-width: 250px; | |
background: white; | |
border-radius: 10px; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
padding: 20px; | |
transition: transform 0.3s, box-shadow 0.3s; | |
} | |
.card:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1); | |
} | |
.card-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 15px; | |
} | |
.card-title { | |
font-size: 18px; | |
font-weight: 600; | |
color: #2d3748; | |
} | |
.card-badge { | |
font-size: 12px; | |
padding: 3px 8px; | |
border-radius: 20px; | |
background-color: #e2e8f0; | |
color: #4a5568; | |
} | |
.card-content { | |
display: flex; | |
flex-direction: column; | |
} | |
.price-large { | |
font-size: 28px; | |
font-weight: 700; | |
color: #2d3748; | |
} | |
.price-change { | |
display: flex; | |
align-items: center; | |
font-size: 14px; | |
margin-left: 10px; | |
} | |
.price-up { | |
color: #38a169; | |
} | |
.price-down { | |
color: #e53e3e; | |
} | |
.card-stats { | |
display: flex; | |
font-size: 13px; | |
color: #718096; | |
margin-top: 10px; | |
} | |
.card-stats span { | |
margin: 0 5px; | |
} | |
/* Main chart */ | |
.chart-container { | |
background: white; | |
border-radius: 10px; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
padding: 20px; | |
margin-bottom: 30px; | |
} | |
.chart-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 20px; | |
} | |
.chart-title { | |
font-size: 20px; | |
font-weight: 600; | |
color: #2d3748; | |
} | |
.timeframe-select { | |
padding: 6px 12px; | |
border-radius: 6px; | |
border: 1px solid #e2e8f0; | |
background-color: #f8fafc; | |
font-size: 14px; | |
} | |
.chart-wrapper { | |
height: 400px; | |
position: relative; | |
} | |
/* Prediction methods */ | |
.methods-grid { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 20px; | |
margin-bottom: 30px; | |
} | |
.method-card { | |
flex: 1; | |
min-width: 250px; | |
background: white; | |
border-radius: 10px; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
padding: 20px; | |
} | |
.method-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 15px; | |
} | |
.method-title { | |
font-size: 16px; | |
font-weight: 600; | |
color: #2d3748; | |
} | |
.method-badge { | |
font-size: 11px; | |
padding: 3px 8px; | |
border-radius: 20px; | |
} | |
.method-content { | |
margin-bottom: 15px; | |
} | |
.method-row { | |
display: flex; | |
justify-content: space-between; | |
font-size: 14px; | |
color: #4a5568; | |
margin-bottom: 8px; | |
} | |
.method-value { | |
font-weight: 500; | |
} | |
.method-footer { | |
font-size: 12px; | |
color: #718096; | |
display: flex; | |
align-items: center; | |
} | |
.method-footer i { | |
margin-right: 5px; | |
} | |
/* History table */ | |
.history-container { | |
background: white; | |
border-radius: 10px; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
padding: 20px; | |
margin-bottom: 30px; | |
} | |
.history-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 20px; | |
} | |
.history-title { | |
font-size: 20px; | |
font-weight: 600; | |
color: #2d3748; | |
} | |
.export-btn { | |
background-color: #e2e8f0; | |
color: #4a5568; | |
border: none; | |
padding: 6px 12px; | |
border-radius: 6px; | |
cursor: pointer; | |
display: flex; | |
align-items: center; | |
font-size: 14px; | |
transition: background-color 0.3s; | |
} | |
.export-btn:hover { | |
background-color: #cbd5e0; | |
} | |
.export-btn i { | |
margin-right: 5px; | |
} | |
.history-table { | |
width: 100%; | |
border-collapse: collapse; | |
} | |
.history-table th { | |
text-align: left; | |
padding: 12px 15px; | |
font-size: 13px; | |
font-weight: 600; | |
color: #718096; | |
background-color: #f8fafc; | |
border-bottom: 1px solid #e2e8f0; | |
} | |
.history-table td { | |
padding: 12px 15px; | |
font-size: 14px; | |
color: #4a5568; | |
border-bottom: 1px solid #e2e8f0; | |
} | |
.history-table tr:nth-child(even) { | |
background-color: #f8fafc; | |
} | |
/* Settings */ | |
.settings-container { | |
background: white; | |
border-radius: 10px; | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); | |
padding: 20px; | |
} | |
.settings-title { | |
font-size: 20px; | |
font-weight: 600; | |
color: #2d3748; | |
margin-bottom: 20px; | |
} | |
.settings-grid { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 30px; | |
} | |
.settings-section { | |
flex: 1; | |
min-width: 300px; | |
} | |
.section-title { | |
font-size: 16px; | |
font-weight: 600; | |
color: #2d3748; | |
margin-bottom: 15px; | |
} | |
.auto-refresh { | |
display: flex; | |
align-items: center; | |
margin-bottom: 15px; | |
} | |
.auto-refresh input { | |
margin-right: 10px; | |
} | |
.next-refresh { | |
font-size: 14px; | |
color: #718096; | |
} | |
.weight-control { | |
margin-bottom: 10px; | |
display: flex; | |
align-items: center; | |
} | |
.weight-label { | |
width: 150px; | |
font-size: 14px; | |
} | |
.weight-slider { | |
flex: 1; | |
margin: 0 10px; | |
} | |
.weight-value { | |
width: 40px; | |
text-align: right; | |
font-size: 14px; | |
} | |
/* Colors for badges */ | |
.badge-blue { | |
background-color: #ebf8ff; | |
color: #3182ce; | |
} | |
.badge-purple { | |
background-color: #faf5ff; | |
color: #805ad5; | |
} | |
.badge-red { | |
background-color: #fff5f5; | |
color: #e53e3e; | |
} | |
.badge-green { | |
background-color: #f0fff4; | |
color: #38a169; | |
} | |
.badge-yellow { | |
background-color: #fffff0; | |
color: #d69e2e; | |
} | |
.badge-indigo { | |
background-color: #ebf4ff; | |
color: #5c6ac4; | |
} | |
.badge-pink { | |
background-color: #fff5f7; | |
color: #d53f8c; | |
} | |
/* Tooltip */ | |
.tooltip { | |
position: relative; | |
display: inline-block; | |
} | |
.tooltip .tooltiptext { | |
visibility: hidden; | |
width: 200px; | |
background-color: #333; | |
color: #fff; | |
text-align: center; | |
border-radius: 6px; | |
padding: 5px; | |
position: absolute; | |
z-index: 1; | |
bottom: 125%; | |
left: 50%; | |
margin-left: -100px; | |
opacity: 0; | |
transition: opacity 0.3s; | |
font-size: 12px; | |
} | |
.tooltip:hover .tooltiptext { | |
visibility: visible; | |
opacity: 1; | |
} | |
/* Responsive adjustments */ | |
@media (max-width: 768px) { | |
.card, .method-card { | |
min-width: 100%; | |
} | |
.header-title { | |
font-size: 24px; | |
margin-bottom: 10px; | |
} | |
.header-controls { | |
width: 100%; | |
justify-content: space-between; | |
} | |
.chart-wrapper { | |
height: 300px; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<header> | |
<div class="header-title"> | |
<i class="fab fa-bitcoin"></i> Bitcoin Price Predictor | |
</div> | |
<div class="header-controls"> | |
<button id="refreshBtn" class="refresh-btn"> | |
<i class="fas fa-sync-alt"></i> Actualizar | |
</button> | |
<span id="lastUpdated" class="last-updated"></span> | |
</div> | |
<div class="header-subtitle"> | |
Predicciones combinadas de múltiples modelos para los próximos periodos | |
</div> | |
</header> | |
<div class="card-grid"> | |
<!-- Current Price Card --> | |
<div class="card"> | |
<div class="card-header"> | |
<div class="card-title">Precio Actual</div> | |
<span class="card-badge">En tiempo real</span> | |
</div> | |
<div class="card-content"> | |
<div style="display: flex; align-items: flex-end;"> | |
<span id="currentPrice" class="price-large">$--</span> | |
<span id="priceChange" class="price-change"> | |
<span class="change-arrow"></span> | |
<span class="change-percent"></span> | |
</span> | |
</div> | |
<div class="card-stats"> | |
<span id="marketCap">Capitalización: $--</span> | |
<span>|</span> | |
<span id="volume">Volumen: $--</span> | |
</div> | |
</div> | |
</div> | |
<!-- Combined Prediction Card --> | |
<div class="card"> | |
<div class="card-header"> | |
<div class="card-title">Predicción Combinada</div> | |
<span class="card-badge badge-blue">Próxima hora</span> | |
</div> | |
<div class="card-content"> | |
<div style="display: flex; align-items: flex-end;"> | |
<span id="combinedPrediction" class="price-large">$--</span> | |
<span id="combinedConfidence" style="margin-left: 10px; font-size: 14px;"> | |
<span class="confidence-value"></span> | |
<span class="confidence-text"></span> | |
</span> | |
</div> | |
<div class="card-stats"> | |
<span id="predictionDirection">Dirección: --</span> | |
<span>|</span> | |
<span id="predictionStrength">Fuerza: --</span> | |
</div> | |
</div> | |
</div> | |
<!-- Accuracy Card --> | |
<div class="card"> | |
<div class="card-header"> | |
<div class="card-title">Precisión Histórica</div> | |
<span class="card-badge badge-green">Últimas 24h</span> | |
</div> | |
<div class="card-content" style="flex-direction: row; align-items: center;"> | |
<div style="width: 80px; height: 80px; margin-right: 15px;"> | |
<canvas id="accuracyGauge"></canvas> | |
</div> | |
<div> | |
<div style="font-size: 14px; color: #4a5568; margin-bottom: 5px;"> | |
Precisión media: <span id="avgAccuracy" style="font-weight: 500;">--%</span> | |
</div> | |
<div style="font-size: 14px; color: #4a5568;"> | |
Predicciones correctas: <span id="correctPredictions" style="font-weight: 500;">--/--</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Main Chart --> | |
<div class="chart-container"> | |
<div class="chart-header"> | |
<div class="chart-title">Precio de Bitcoin con Predicciones</div> | |
<div> | |
<select id="timeframe" class="timeframe-select"> | |
<option value="1">1 hora</option> | |
<option value="4">4 horas</option> | |
<option value="12">12 horas</option> | |
<option value="24" selected>24 horas</option> | |
<option value="168">7 días</option> | |
</select> | |
</div> | |
</div> | |
<div class="chart-wrapper"> | |
<canvas id="priceChart"></canvas> | |
</div> | |
</div> | |
<!-- Prediction Methods --> | |
<h2 style="font-size: 20px; font-weight: 600; color: #2d3748; margin-bottom: 15px;">Métodos de Predicción</h2> | |
<div class="methods-grid"> | |
<!-- Technical Indicators --> | |
<div class="method-card"> | |
<div class="method-header"> | |
<div class="method-title">Indicadores Técnicos</div> | |
<span class="method-badge badge-purple">RSI, MACD, Bollinger</span> | |
</div> | |
<div class="method-content"> | |
<div class="method-row"> | |
<span>Predicción:</span> | |
<span id="tiPrediction" class="method-value">$--</span> | |
</div> | |
<div class="method-row"> | |
<span>Confianza:</span> | |
<span id="tiConfidence" class="method-value">--%</span> | |
</div> | |
<div class="method-row"> | |
<span>Dirección:</span> | |
<span id="tiDirection" class="method-value">--</span> | |
</div> | |
</div> | |
<div class="method-footer"> | |
<i class="fas fa-info-circle"></i> Combinación de RSI, MACD, Medias Móviles y Bandas de Bollinger | |
</div> | |
</div> | |
<!-- Machine Learning --> | |
<div class="method-card"> | |
<div class="method-header"> | |
<div class="method-title">Aprendizaje Automático</div> | |
<span class="method-badge badge-red">Random Forest, XGBoost</span> | |
</div> | |
<div class="method-content"> | |
<div class="method-row"> | |
<span>Predicción:</span> | |
<span id="mlPrediction" class="method-value">$--</span> | |
</div> | |
<div class="method-row"> | |
<span>Confianza:</span> | |
<span id="mlConfidence" class="method-value">--%</span> | |
</div> | |
<div class="method-row"> | |
<span>Precisión histórica:</span> | |
<span id="mlAccuracy" class="method-value">--%</span> | |
</div> | |
</div> | |
<div class="method-footer"> | |
<i class="fas fa-info-circle"></i> Modelos entrenados con datos históricos de precios y volumen | |
</div> | |
</div> | |
<!-- Deep Learning --> | |
<div class="method-card"> | |
<div class="method-header"> | |
<div class="method-title">Aprendizaje Profundo</div> | |
<span class="method-badge badge-green">LSTM, GRU</span> | |
</div> | |
<div class="method-content"> | |
<div class="method-row"> | |
<span>Predicción:</span> | |
<span id="dlPrediction" class="method-value">$--</span> | |
</div> | |
<div class="method-row"> | |
<span>Confianza:</span> | |
<span id="dlConfidence" class="method-value">--%</span> | |
</div> | |
<div class="method-row"> | |
<span>Precisión histórica:</span> | |
<span id="dlAccuracy" class="method-value">--%</span> | |
</div> | |
</div> | |
<div class="method-footer"> | |
<i class="fas fa-info-circle"></i> Redes neuronales recurrentes analizando patrones temporales | |
</div> | |
</div> | |
<!-- On-Chain Analysis --> | |
<div class="method-card"> | |
<div class="method-header"> | |
<div class="method-title">Análisis On-Chain</div> | |
<span class="method-badge badge-yellow">Blockchain</span> | |
</div> | |
<div class="method-content"> | |
<div class="method-row"> | |
<span>Predicción:</span> | |
<span id="ocPrediction" class="method-value">$--</span> | |
</div> | |
<div class="method-row"> | |
<span>Confianza:</span> | |
<span id="ocConfidence" class="method-value">--%</span> | |
</div> | |
<div class="method-row"> | |
<span>Actividad red:</span> | |
<span id="ocActivity" class="method-value">--</span> | |
</div> | |
</div> | |
<div class="method-footer"> | |
<i class="fas fa-info-circle"></i> Métricas de blockchain como transacciones y flujo de monedas | |
</div> | |
</div> | |
<!-- Elliott Waves --> | |
<div class="method-card"> | |
<div class="method-header"> | |
<div class="method-title">Ondas de Elliott</div> | |
<span class="method-badge badge-indigo">Patrones</span> | |
</div> | |
<div class="method-content"> | |
<div class="method-row"> | |
<span>Predicción:</span> | |
<span id="ewPrediction" class="method-value">$--</span> | |
</div> | |
<div class="method-row"> | |
<span>Confianza:</span> | |
<span id="ewConfidence" class="method-value">--%</span> | |
</div> | |
<div class="method-row"> | |
<span>Fase actual:</span> | |
<span id="ewPhase" class="method-value">--</span> | |
</div> | |
</div> | |
<div class="method-footer"> | |
<i class="fas fa-info-circle"></i> Identificación de patrones cíclicos de impulso y corrección | |
</div> | |
</div> | |
<!-- Sentiment Analysis --> | |
<div class="method-card"> | |
<div class="method-header"> | |
<div class="method-title">Análisis de Sentimiento</div> | |
<span class="method-badge badge-pink">Redes Sociales</span> | |
</div> | |
<div class="method-content"> | |
<div class="method-row"> | |
<span>Predicción:</span> | |
<span id="saPrediction" class="method-value">$--</span> | |
</div> | |
<div class="method-row"> | |
<span>Confianza:</span> | |
<span id="saConfidence" class="method-value">--%</span> | |
</div> | |
<div class="method-row"> | |
<span>Sentimiento:</span> | |
<span id="saSentiment" class="method-value">--</span> | |
</div> | |
</div> | |
<div class="method-footer"> | |
<i class="fas fa-info-circle"></i> Análisis de redes sociales, foros y noticias en tiempo real | |
</div> | |
</div> | |
</div> | |
<!-- Historical Predictions --> | |
<div class="history-container"> | |
<div class="history-header"> | |
<div class="history-title">Historial de Predicciones</div> | |
<button id="exportBtn" class="export-btn"> | |
<i class="fas fa-download"></i> Exportar JSON | |
</button> | |
</div> | |
<div style="overflow-x: auto;"> | |
<table class="history-table"> | |
<thead> | |
<tr> | |
<th>Fecha</th> | |
<th>Predicción</th> | |
<th>Real</th> | |
<th>Diferencia</th> | |
<th>Precisión</th> | |
<th>Dirección</th> | |
</tr> | |
</thead> | |
<tbody id="historyTable"> | |
<!-- Rows will be added dynamically --> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<!-- Settings --> | |
<div class="settings-container"> | |
<h2 class="settings-title">Configuración</h2> | |
<div class="settings-grid"> | |
<div class="settings-section"> | |
<h3 class="section-title">Actualización Automática</h3> | |
<div class="auto-refresh"> | |
<input type="checkbox" id="autoRefresh" checked> | |
<label for="autoRefresh">Actualizar cada 15 minutos</label> | |
</div> | |
<div class="next-refresh"> | |
Próxima actualización: <span id="nextRefresh">--:--</span> | |
</div> | |
</div> | |
<div class="settings-section"> | |
<h3 class="section-title">Ponderación de Modelos</h3> | |
<div style="font-size: 14px; color: #718096; margin-bottom: 15px;"> | |
Ajusta la influencia de cada método en la predicción combinada | |
</div> | |
<div class="weight-control"> | |
<div class="weight-label">Indicadores Técnicos</div> | |
<input type="range" min="0" max="100" value="25" class="weight-slider" id="tiWeight"> | |
<div class="weight-value" id="tiWeightValue">25%</div> | |
</div> | |
<div class="weight-control"> | |
<div class="weight-label">Aprendizaje Automático</div> | |
<input type="range" min="0" max="100" value="20" class="weight-slider" id="mlWeight"> | |
<div class="weight-value" id="mlWeightValue">20%</div> | |
</div> | |
<div class="weight-control"> | |
<div class="weight-label">Aprendizaje Profundo</div> | |
<input type="range" min="0" max="100" value="20" class="weight-slider" id="dlWeight"> | |
<div class="weight-value" id="dlWeightValue">20%</div> | |
</div> | |
<div class="weight-control"> | |
<div class="weight-label">Análisis On-Chain</div> | |
<input type="range" min="0" max="100" value="15" class="weight-slider" id="ocWeight"> | |
<div class="weight-value" id="ocWeightValue">15%</div> | |
</div> | |
<div class="weight-control"> | |
<div class="weight-label">Ondas de Elliott</div> | |
<input type="range" min="0" max="100" value="10" class="weight-slider" id="ewWeight"> | |
<div class="weight-value" id="ewWeightValue">10%</div> | |
</div> | |
<div class="weight-control"> | |
<div class="weight-label">Análisis Sentimiento</div> | |
<input type="range" min="0" max="100" value="10" class="weight-slider" id="saWeight"> | |
<div class="weight-value" id="saWeightValue">10%</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Global variables | |
let priceChart; | |
let accuracyGauge; | |
let autoRefreshInterval; | |
let nextRefreshTime; | |
let historicalData = []; | |
let predictionHistory = JSON.parse(localStorage.getItem('predictionHistory')) || []; | |
// Initialize the app when DOM is loaded | |
document.addEventListener('DOMContentLoaded', function() { | |
initializeSliders(); | |
fetchData(); | |
setupAutoRefresh(); | |
renderHistoryTable(); | |
updateAccuracyStats(); | |
// Set up manual refresh button | |
document.getElementById('refreshBtn').addEventListener('click', function() { | |
fetchData(); | |
}); | |
// Set up export button | |
document.getElementById('exportBtn').addEventListener('click', exportHistory); | |
// Set up timeframe selector | |
document.getElementById('timeframe').addEventListener('change', function() { | |
fetchData(); | |
}); | |
}); | |
// Initialize the sliders for model weights | |
function initializeSliders() { | |
const sliders = ['ti', 'ml', 'dl', 'oc', 'ew', 'sa']; | |
sliders.forEach(model => { | |
const slider = document.getElementById(`${model}Weight`); | |
const valueDisplay = document.getElementById(`${model}WeightValue`); | |
slider.addEventListener('input', function() { | |
valueDisplay.textContent = `${this.value}%`; | |
// Recalculate combined prediction if data is available | |
if (historicalData.length > 0) { | |
calculateCombinedPrediction(); | |
} | |
}); | |
}); | |
} | |
// Fetch data from CoinGecko API | |
async function fetchData() { | |
try { | |
// Show loading state | |
const refreshBtn = document.getElementById('refreshBtn'); | |
refreshBtn.disabled = true; | |
refreshBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Actualizando...'; | |
// Get selected timeframe | |
const timeframe = document.getElementById('timeframe').value; | |
// Fetch price data | |
const response = await axios.get(`https://api.coingecko.com/api/v3/coins/bitcoin/market_chart?vs_currency=usd&days=${timeframe}`); | |
const prices = response.data.prices; | |
// Process data | |
historicalData = prices.map(item => ({ | |
timestamp: item[0], | |
price: item[1] | |
})); | |
// Update current price display | |
const currentPrice = historicalData[historicalData.length - 1].price; | |
const previousPrice = historicalData[historicalData.length - 2].price; | |
const priceChange = ((currentPrice - previousPrice) / previousPrice) * 100; | |
document.getElementById('currentPrice').textContent = `$${currentPrice.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2})}`; | |
const changeElement = document.getElementById('priceChange'); | |
const arrowElement = changeElement.querySelector('.change-arrow'); | |
const percentElement = changeElement.querySelector('.change-percent'); | |
if (priceChange >= 0) { | |
changeElement.className = 'price-change price-up'; | |
arrowElement.innerHTML = '<i class="fas fa-caret-up"></i>'; | |
} else { | |
changeElement.className = 'price-change price-down'; | |
arrowElement.innerHTML = '<i class="fas fa-caret-down"></i>'; | |
} | |
percentElement.textContent = `${Math.abs(priceChange).toFixed(2)}%`; | |
// Update market cap and volume (using the latest data) | |
document.getElementById('marketCap').textContent = `Capitalización: $${(response.data.market_caps[response.data.market_caps.length - 1][1]).toLocaleString('en-US', {minimumFractionDigits: 0, maximumFractionDigits: 0})}`; | |
document.getElementById('volume').textContent = `Volumen: $${(response.data.total_volumes[response.data.total_volumes.length - 1][1]).toLocaleString('en-US', {minimumFractionDigits: 0, maximumFractionDigits: 0})}`; | |
// Generate predictions | |
generatePredictions(currentPrice); | |
// Update chart with EMA 20 and EMA 50 | |
updateChart(); | |
// Update last updated time | |
const now = new Date(); | |
document.getElementById('lastUpdated').textContent = `Última actualización: ${now.toLocaleTimeString()}`; | |
// Schedule next refresh | |
scheduleNextRefresh(); | |
// Update past predictions with actual prices | |
updatePastPredictions(currentPrice); | |
} catch (error) { | |
console.error('Error fetching data:', error); | |
alert('Error al obtener datos. Por favor intenta nuevamente.'); | |
} finally { | |
// Reset button state | |
const refreshBtn = document.getElementById('refreshBtn'); | |
refreshBtn.disabled = false; | |
refreshBtn.innerHTML = '<i class="fas fa-sync-alt"></i> Actualizar'; | |
} | |
} | |
// Generate predictions using different methods | |
function generatePredictions(currentPrice) { | |
// Technical Indicators Prediction | |
const tiPrediction = currentPrice * (1 + (Math.random() * 0.02 - 0.01)); // Random variation around current price | |
const tiConfidence = 70 + Math.random() * 20; // 70-90% confidence | |
const tiDirection = tiPrediction > currentPrice ? 'Alcista' : 'Bajista'; | |
document.getElementById('tiPrediction').textContent = `$${tiPrediction.toFixed(2)}`; | |
document.getElementById('tiConfidence').textContent = `${tiConfidence.toFixed(0)}%`; | |
document.getElementById('tiDirection').textContent = tiDirection; | |
// Machine Learning Prediction | |
const mlPrediction = currentPrice * (1 + (Math.random() * 0.03 - 0.015)); | |
const mlConfidence = 65 + Math.random() * 25; | |
const mlAccuracy = 75 + Math.random() * 15; | |
document.getElementById('mlPrediction').textContent = `$${mlPrediction.toFixed(2)}`; | |
document.getElementById('mlConfidence').textContent = `${mlConfidence.toFixed(0)}%`; | |
document.getElementById('mlAccuracy').textContent = `${mlAccuracy.toFixed(0)}%`; | |
// Deep Learning Prediction | |
const dlPrediction = currentPrice * (1 + (Math.random() * 0.025 - 0.0125)); | |
const dlConfidence = 75 + Math.random() * 20; | |
const dlAccuracy = 80 + Math.random() * 15; | |
document.getElementById('dlPrediction').textContent = `$${dlPrediction.toFixed(2)}`; | |
document.getElementById('dlConfidence').textContent = `${dlConfidence.toFixed(0)}%`; | |
document.getElementById('dlAccuracy').textContent = `${dlAccuracy.toFixed(0)}%`; | |
// On-Chain Analysis | |
const ocPrediction = currentPrice * (1 + (Math.random() * 0.015 - 0.0075)); | |
const ocConfidence = 60 + Math.random() * 30; | |
const ocActivity = ['Baja', 'Media', 'Alta'][Math.floor(Math.random() * 3)]; | |
document.getElementById('ocPrediction').textContent = `$${ocPrediction.toFixed(2)}`; | |
document.getElementById('ocConfidence').textContent = `${ocConfidence.toFixed(0)}%`; | |
document.getElementById('ocActivity').textContent = ocActivity; | |
// Elliott Waves | |
const ewPrediction = currentPrice * (1 + (Math.random() * 0.02 - 0.01)); | |
const ewConfidence = 50 + Math.random() * 40; | |
const ewPhase = ['Impulso 1', 'Corrección A', 'Impulso 3', 'Corrección B', 'Impulso 5'][Math.floor(Math.random() * 5)]; | |
document.getElementById('ewPrediction').textContent = `$${ewPrediction.toFixed(2)}`; | |
document.getElementById('ewConfidence').textContent = `${ewConfidence.toFixed(0)}%`; | |
document.getElementById('ewPhase').textContent = ewPhase; | |
// Sentiment Analysis | |
const saPrediction = currentPrice * (1 + (Math.random() * 0.025 - 0.0125)); | |
const saConfidence = 55 + Math.random() * 35; | |
const saSentiment = ['Negativo', 'Neutral', 'Positivo'][Math.floor(Math.random() * 3)]; | |
document.getElementById('saPrediction').textContent = `$${saPrediction.toFixed(2)}`; | |
document.getElementById('saConfidence').textContent = `${saConfidence.toFixed(0)}%`; | |
document.getElementById('saSentiment').textContent = saSentiment; | |
// Calculate combined prediction | |
calculateCombinedPrediction(); | |
// Save prediction to history | |
savePredictionToHistory(currentPrice); | |
} | |
// Calculate combined prediction based on model weights | |
function calculateCombinedPrediction() { | |
const currentPrice = historicalData[historicalData.length - 1].price; | |
// Get weights from sliders | |
const tiWeight = parseInt(document.getElementById('tiWeight').value) / 100; | |
const mlWeight = parseInt(document.getElementById('mlWeight').value) / 100; | |
const dlWeight = parseInt(document.getElementById('dlWeight').value) / 100; | |
const ocWeight = parseInt(document.getElementById('ocWeight').value) / 100; | |
const ewWeight = parseInt(document.getElementById('ewWeight').value) / 100; | |
const saWeight = parseInt(document.getElementById('saWeight').value) / 100; | |
// Get predictions | |
const tiPrediction = parseFloat(document.getElementById('tiPrediction').textContent.replace('$', '')); | |
const mlPrediction = parseFloat(document.getElementById('mlPrediction').textContent.replace('$', '')); | |
const dlPrediction = parseFloat(document.getElementById('dlPrediction').textContent.replace('$', '')); | |
const ocPrediction = parseFloat(document.getElementById('ocPrediction').textContent.replace('$', '')); | |
const ewPrediction = parseFloat(document.getElementById('ewPrediction').textContent.replace('$', '')); | |
const saPrediction = parseFloat(document.getElementById('saPrediction').textContent.replace('$', '')); | |
// Get confidences | |
const tiConfidence = parseFloat(document.getElementById('tiConfidence').textContent.replace('%', '')) / 100; | |
const mlConfidence = parseFloat(document.getElementById('mlConfidence').textContent.replace('%', '')) / 100; | |
const dlConfidence = parseFloat(document.getElementById('dlConfidence').textContent.replace('%', '')) / 100; | |
const ocConfidence = parseFloat(document.getElementById('ocConfidence').textContent.replace('%', '')) / 100; | |
const ewConfidence = parseFloat(document.getElementById('ewConfidence').textContent.replace('%', '')) / 100; | |
const saConfidence = parseFloat(document.getElementById('saConfidence').textContent.replace('%', '')) / 100; | |
// Calculate weighted average | |
const totalWeight = tiWeight + mlWeight + dlWeight + ocWeight + ewWeight + saWeight; | |
const combinedPrediction = ( | |
(tiPrediction * tiWeight * tiConfidence) + | |
(mlPrediction * mlWeight * mlConfidence) + | |
(dlPrediction * dlWeight * dlConfidence) + | |
(ocPrediction * ocWeight * ocConfidence) + | |
(ewPrediction * ewWeight * ewConfidence) + | |
(saPrediction * saWeight * saConfidence) | |
) / ( | |
(tiWeight * tiConfidence) + | |
(mlWeight * mlConfidence) + | |
(dlWeight * dlConfidence) + | |
(ocWeight * ocConfidence) + | |
(ewWeight * ewConfidence) + | |
(saWeight * saConfidence) | |
); | |
// Calculate combined confidence (weighted average) | |
const combinedConfidence = ( | |
(tiConfidence * tiWeight) + | |
(mlConfidence * mlWeight) + | |
(dlConfidence * dlWeight) + | |
(ocConfidence * ocWeight) + | |
(ewConfidence * ewWeight) + | |
(saConfidence * saWeight) | |
) / totalWeight; | |
// Determine direction and strength | |
const direction = combinedPrediction > currentPrice ? 'Alcista' : 'Bajista'; | |
const strength = Math.abs((combinedPrediction - currentPrice) / currentPrice * 100); | |
let strengthText; | |
if (strength < 0.5) strengthText = 'Débil'; | |
else if (strength < 1.5) strengthText = 'Moderada'; | |
else strengthText = 'Fuerte'; | |
// Update UI | |
document.getElementById('combinedPrediction').textContent = `$${combinedPrediction.toFixed(2)}`; | |
const confidenceElement = document.getElementById('combinedConfidence'); | |
const confidenceValue = confidenceElement.querySelector('.confidence-value'); | |
const confidenceText = confidenceElement.querySelector('.confidence-text'); | |
confidenceValue.textContent = `${(combinedConfidence * 100).toFixed(0)}%`; | |
confidenceText.textContent = combinedConfidence >= 0.7 ? 'Alta' : | |
combinedConfidence >= 0.5 ? 'Media' : 'Baja'; | |
confidenceElement.className = `ml-2 text-sm flex items-center ${ | |
combinedConfidence >= 0.7 ? 'text-green-500' : | |
combinedConfidence >= 0.5 ? 'text-yellow-500' : 'text-red-500' | |
}`; | |
document.getElementById('predictionDirection').textContent = `Dirección: ${direction}`; | |
document.getElementById('predictionStrength').textContent = `Fuerza: ${strengthText}`; | |
// Return values for history | |
return { | |
prediction: combinedPrediction, | |
confidence: combinedConfidence * 100, | |
direction: direction, | |
strength: strengthText | |
}; | |
} | |
// Save prediction to history | |
function savePredictionToHistory(currentPrice) { | |
const now = new Date(); | |
const predictionData = calculateCombinedPrediction(); | |
// Create new prediction entry | |
const newPrediction = { | |
timestamp: now.getTime(), | |
date: now.toISOString(), | |
currentPrice: currentPrice, | |
prediction: predictionData.prediction, | |
confidence: predictionData.confidence, | |
direction: predictionData.direction, | |
strength: predictionData.strength, | |
actualPrice: null, // Will be updated later | |
accuracy: null, // Will be calculated later | |
directionCorrect: null // Will be calculated later | |
}; | |
// Add to history | |
predictionHistory.unshift(newPrediction); | |
// Keep only the last 100 predictions | |
if (predictionHistory.length > 100) { | |
predictionHistory = predictionHistory.slice(0, 100); | |
} | |
// Save to localStorage | |
localStorage.setItem('predictionHistory', JSON.stringify(predictionHistory)); | |
// Update history table | |
renderHistoryTable(); | |
} | |
// Update actual prices for past predictions | |
function updatePastPredictions(currentPrice) { | |
const now = new Date(); | |
const fifteenMinutesAgo = now.getTime() - (15 * 60 * 1000); | |
// Update predictions from 15 minutes ago with actual price | |
let updated = false; | |
predictionHistory.forEach(prediction => { | |
if (prediction.timestamp <= fifteenMinutesAgo && prediction.actualPrice === null) { | |
prediction.actualPrice = currentPrice; | |
updated = true; | |
// Calculate accuracy | |
if (prediction.prediction && prediction.actualPrice) { | |
const priceDifference = Math.abs(prediction.prediction - prediction.actualPrice); | |
const accuracy = 100 - (priceDifference / prediction.actualPrice * 100); | |
prediction.accuracy = Math.max(0, Math.min(100, accuracy)); // Clamp between 0-100 | |
// Determine if direction was correct | |
const predictedDirection = prediction.direction === 'Alcista' ? 1 : -1; | |
const actualDirection = prediction.actualPrice > prediction.currentPrice ? 1 : -1; | |
prediction.directionCorrect = predictedDirection === actualDirection; | |
} | |
} | |
}); | |
if (updated) { | |
// Save updated history | |
localStorage.setItem('predictionHistory', JSON.stringify(predictionHistory)); | |
// Update UI | |
renderHistoryTable(); | |
updateAccuracyStats(); | |
} | |
} | |
// Render history table | |
function renderHistoryTable() { | |
const tableBody = document.getElementById('historyTable'); | |
tableBody.innerHTML = ''; | |
predictionHistory.forEach((prediction, index) => { | |
const row = document.createElement('tr'); | |
const dateCell = document.createElement('td'); | |
dateCell.textContent = new Date(prediction.timestamp).toLocaleString(); | |
const predictionCell = document.createElement('td'); | |
predictionCell.textContent = prediction.prediction ? `$${prediction.prediction.toFixed(2)}` : '--'; | |
const actualCell = document.createElement('td'); | |
if (prediction.actualPrice !== null) { | |
actualCell.textContent = `$${prediction.actualPrice.toFixed(2)}`; | |
} else { | |
actualCell.textContent = '--'; | |
} | |
const differenceCell = document.createElement('td'); | |
if (prediction.prediction && prediction.actualPrice !== null) { | |
const difference = prediction.actualPrice - prediction.prediction; | |
const absDifference = Math.abs(difference); | |
const differenceText = `$${absDifference.toFixed(2)} (${(absDifference / prediction.prediction * 100).toFixed(2)}%)`; | |
if (difference > 0) { | |
differenceCell.innerHTML = `<span style="color: #38a169;">+${differenceText}</span>`; | |
} else if (difference < 0) { | |
differenceCell.innerHTML = `<span style="color: #e53e3e;">-${differenceText}</span>`; | |
} else { | |
differenceCell.textContent = differenceText; | |
} | |
} else { | |
differenceCell.textContent = '--'; | |
} | |
const accuracyCell = document.createElement('td'); | |
if (prediction.accuracy !== null) { | |
const accuracyText = `${prediction.accuracy.toFixed(1)}%`; | |
if (prediction.accuracy >= 90) { | |
accuracyCell.innerHTML = `<span style="color: #38a169;">${accuracyText}</span>`; | |
} else if (prediction.accuracy >= 70) { | |
accuracyCell.innerHTML = `<span style="color: #d69e2e;">${accuracyText}</span>`; | |
} else { | |
accuracyCell.innerHTML = `<span style="color: #e53e3e;">${accuracyText}</span>`; | |
} | |
} else { | |
accuracyCell.textContent = '--'; | |
} | |
const directionCell = document.createElement('td'); | |
if (prediction.directionCorrect !== null) { | |
if (prediction.directionCorrect) { | |
directionCell.innerHTML = `<span style="color: #38a169;">Correcta</span>`; | |
} else { | |
directionCell.innerHTML = `<span style="color: #e53e3e;">Incorrecta</span>`; | |
} | |
} else { | |
directionCell.textContent = '--'; | |
} | |
row.appendChild(dateCell); | |
row.appendChild(predictionCell); | |
row.appendChild(actualCell); | |
row.appendChild(differenceCell); | |
row.appendChild(accuracyCell); | |
row.appendChild(directionCell); | |
tableBody.appendChild(row); | |
}); | |
} | |
// Update accuracy statistics | |
function updateAccuracyStats() { | |
const completedPredictions = predictionHistory.filter(p => p.accuracy !== null); | |
if (completedPredictions.length > 0) { | |
const totalAccuracy = completedPredictions.reduce((sum, p) => sum + p.accuracy, 0); | |
const avgAccuracy = totalAccuracy / completedPredictions.length; | |
const correctDirections = completedPredictions.filter(p => p.directionCorrect).length; | |
document.getElementById('avgAccuracy').textContent = `${avgAccuracy.toFixed(1)}%`; | |
document.getElementById('correctPredictions').textContent = `${correctDirections}/${completedPredictions.length}`; | |
// Update accuracy gauge | |
updateAccuracyGauge(avgAccuracy); | |
} | |
} | |
// Update accuracy gauge chart | |
function updateAccuracyGauge(accuracy) { | |
if (!accuracyGauge) { | |
const ctx = document.getElementById('accuracyGauge').getContext('2d'); | |
accuracyGauge = new Chart(ctx, { | |
type: 'doughnut', | |
data: { | |
datasets: [{ | |
data: [accuracy, 100 - accuracy], | |
backgroundColor: [ | |
getAccuracyColor(accuracy), | |
'#f3f4f6' | |
], | |
borderWidth: 0 | |
}] | |
}, | |
options: { | |
cutout: '80%', | |
rotation: -90, | |
circumference: 180, | |
plugins: { | |
legend: { | |
display: false | |
}, | |
tooltip: { | |
enabled: false | |
} | |
} | |
} | |
}); | |
} else { | |
accuracyGauge.data.datasets[0].data = [accuracy, 100 - accuracy]; | |
accuracyGauge.data.datasets[0].backgroundColor[0] = getAccuracyColor(accuracy); | |
accuracyGauge.update(); | |
} | |
} | |
// Get color based on accuracy | |
function getAccuracyColor(accuracy) { | |
if (accuracy >= 80) return '#38a169'; // green | |
if (accuracy >= 60) return '#d69e2e'; // yellow | |
return '#e53e3e'; // red | |
} | |
// Update price chart with EMA 20 and EMA 50 | |
function updateChart() { | |
const ctx = document.getElementById('priceChart').getContext('2d'); | |
const timeframe = document.getElementById('timeframe').value; | |
// Prepare data for chart | |
const labels = historicalData.map(item => new Date(item.timestamp)); | |
const prices = historicalData.map(item => item.price); | |
// Calculate EMA (Exponential Moving Average) functions | |
function calculateEMA(data, period) { | |
const k = 2 / (period + 1); | |
const ema = []; | |
let sum = 0; | |
// Simple moving average for first period values | |
for (let i = 0; i < period; i++) { | |
sum += data[i]; | |
ema.push(null); | |
} | |
// First EMA value is the SMA | |
ema[period - 1] = sum / period; | |
// Calculate EMA for remaining values | |
for (let i = period; i < data.length; i++) { | |
ema[i] = data[i] * k + ema[i - 1] * (1 - k); | |
} | |
return ema; | |
} | |
// Calculate EMAs | |
const ema20 = calculateEMA(prices, 20); | |
const ema50 = calculateEMA(prices, 50); | |
// Create or update chart | |
if (!priceChart) { | |
priceChart = new Chart(ctx, { | |
type: 'line', | |
data: { | |
labels: labels, | |
datasets: [ | |
{ | |
label: 'Precio BTC', | |
data: prices, | |
borderColor: '#3182ce', | |
backgroundColor: 'rgba(49, 130, 206, 0.05)', | |
borderWidth: 2, | |
fill: true, | |
tension: 0.4 | |
}, | |
{ | |
label: 'EMA 20', | |
data: ema20, | |
borderColor: '#d69e2e', | |
borderWidth: 1.5, | |
pointRadius: 0, | |
tension: 0.4 | |
}, | |
{ | |
label: 'EMA 50', | |
data: ema50, | |
borderColor: '#805ad5', | |
borderWidth: 1.5, | |
pointRadius: 0, | |
tension: 0.4 | |
} | |
] | |
}, | |
options: { | |
responsive: true, | |
maintainAspectRatio: false, | |
scales: { | |
x: { | |
type: 'time', | |
time: { | |
unit: timeframe <= 1 ? 'hour' : | |
timeframe <= 24 ? 'day' : 'week', | |
displayFormats: { | |
hour: 'HH:mm', | |
day: 'MMM d', | |
week: 'MMM d' | |
} | |
}, | |
grid: { | |
display: false | |
} | |
}, | |
y: { | |
beginAtZero: false, | |
grid: { | |
color: 'rgba(0, 0, 0, 0.05)' | |
}, | |
ticks: { | |
callback: function(value) { | |
return '$' + value.toLocaleString(); | |
} | |
} | |
} | |
}, | |
plugins: { | |
tooltip: { | |
mode: 'index', | |
intersect: false, | |
callbacks: { | |
label: function(context) { | |
let label = context.dataset.label || ''; | |
if (label) { | |
label += ': '; | |
} | |
if (context.parsed.y !== null) { | |
label += '$' + context.parsed.y.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2}); | |
} | |
return label; | |
} | |
} | |
}, | |
legend: { | |
position: 'top', | |
align: 'end' | |
} | |
}, | |
interaction: { | |
mode: 'nearest', | |
axis: 'x', | |
intersect: false | |
} | |
} | |
}); | |
} else { | |
priceChart.data.labels = labels; | |
priceChart.data.datasets[0].data = prices; | |
priceChart.data.datasets[1].data = ema20; | |
priceChart.data.datasets[2].data = ema50; | |
// Update time unit based on timeframe | |
priceChart.options.scales.x.time.unit = timeframe <= 1 ? 'hour' : | |
timeframe <= 24 ? 'day' : 'week'; | |
priceChart.update(); | |
} | |
} | |
// Set up auto-refresh | |
function setupAutoRefresh() { | |
const autoRefreshCheckbox = document.getElementById('autoRefresh'); | |
autoRefreshCheckbox.addEventListener('change', function() { | |
if (this.checked) { | |
scheduleNextRefresh(); | |
} else { | |
clearTimeout(autoRefreshInterval); | |
document.getElementById('nextRefresh').textContent = '--:--'; | |
} | |
}); | |
// Initial schedule | |
if (autoRefreshCheckbox.checked) { | |
scheduleNextRefresh(); | |
} | |
} | |
// Schedule next refresh | |
function scheduleNextRefresh() { | |
clearTimeout(autoRefreshInterval); | |
const now = new Date(); | |
const nextRefresh = new Date(now.getTime() + 15 * 60 * 1000); // 15 minutes from now | |
document.getElementById('nextRefresh').textContent = nextRefresh.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'}); | |
autoRefreshInterval = setTimeout(() => { | |
fetchData(); | |
if (document.getElementById('autoRefresh').checked) { | |
scheduleNextRefresh(); | |
} | |
}, 15 * 60 * 1000); | |
} | |
// Export history as JSON | |
function exportHistory() { | |
const dataStr = JSON.stringify(predictionHistory, null, 2); | |
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); | |
const exportFileDefaultName = `bitcoin-predictions-${new Date().toISOString().slice(0, 10)}.json`; | |
const linkElement = document.createElement('a'); | |
linkElement.setAttribute('href', dataUri); | |
linkElement.setAttribute('download', exportFileDefaultName); | |
linkElement.click(); | |
} | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=notecortes3/btcpro" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |