import dash
from dash import dcc, html
from dash.dependencies import Input, Output, State
import pandas as pd
import plotly.graph_objects as go
import numpy as np
from datetime import datetime, timedelta
# --- 1. Generación de Datos Simulados (Mock Data) ---
def generar_datos_precios(dias=30):
"""Genera un DataFrame simulado de precios diarios."""
# Crear un rango de fechas
fechas = [datetime.now() - timedelta(days=i) for i in range(dias)][::-1]
# Simular una trayectoria de precios (ejemplo: un paseo aleatorio)
# Empezamos en 100 y añadimos ruido
precios = [100.0]
for _ in range(dias - 1):
# El cambio es un número aleatorio pequeño (-1 a 1)
cambio = np.random.uniform(-1, 1) * 0.5
nuevo_precio = precios[-1] + cambio
# Nos aseguramos de que el precio no baje de 90
precios.append(max(90, nuevo_precio))
df = pd.DataFrame({
'Fecha': fechas,
'Precio': [float(f'{p:.2f}') for p in precios] # Formato a 2 decimales
})
return df
df_precios = generar_datos_precios()
precio_actual = df_precios['Precio'].iloc[-1]
# --- 2. Inicialización de la Aplicación Dash ---
app = dash.Dash(__name__)
# --- 3. Diseño (Layout) de la Aplicación ---
app.layout = html.Div(style={'backgroundColor': '#1f2630', 'color': '#c7d0d9', 'padding': '20px'}, children=[
html.H1("Simulador de Trading con Dash/Plotly", style={'textAlign': 'center', 'color': '#00bcd4'}),
html.P(f"Precio Actual del Activo (CEJ): ${precio_actual:.2f}", id='precio-actual-display', style={'textAlign': 'center', 'fontSize': '1.5em'}),
# Gráfico interactivo (Candlestick o Línea)
dcc.Graph(id='grafico-precios', style={'height': '60vh'}),
html.Div([
dcc.Input(id='input-cantidad', type='number', value=1, min=1, style={'marginRight': '10px', 'padding': '10px', 'width': '100px'}),
html.Button('COMPRAR ACTIVO', id='boton-comprar', n_clicks=0, style={'padding': '10px 20px', 'backgroundColor': '#4CAF50', 'color': 'white', 'border': 'none', 'borderRadius': '5px', 'cursor': 'pointer'}),
], style={'textAlign': 'center', 'marginTop': '20px'}),
html.Div(id='output-transaccion', style={'textAlign': 'center', 'marginTop': '15px', 'fontSize': '1.2em', 'minHeight': '30px'})
])
# --- 4. Callbacks para la Interactividad ---
# Callback para actualizar el gráfico
@app.callback(
Output('grafico-precios', 'figure'),
[Input('grafico-precios', 'relayoutData')] # Esto es solo para que Dash sepa cuándo refrescar (en apps reales, usarías dcc.Interval)
)
def actualizar_grafico(relayout_data):
"""Dibuja un gráfico de línea simple con los precios simulados."""
fig = go.Figure(data=[
go.Scatter(
x=df_precios['Fecha'],
y=df_precios['Precio'],
mode='lines+markers',
name='Precio',
line={'color': '#00bcd4'} # Color del gráfico
)
])
fig.update_layout(
title='Histórico de Precios del Activo (CEJ)',
xaxis_title='Fecha',
yaxis_title='Precio (USD)',
plot_bgcolor='#273142', # Fondo del gráfico
paper_bgcolor='#1f2630', # Fondo general de la figura
font_color='#c7d0d9'
)
return fig
# Callback para manejar la compra
@app.callback(
[Output('output-transaccion', 'children'),
Output('precio-actual-display', 'children')],
[Input('boton-comprar', 'n_clicks')],
[State('input-cantidad', 'value')]
)
def manejar_transaccion(n_clicks, cantidad):
global precio_actual
# Esta función se dispara al inicio, ignoramos el primer click (n_clicks=0)
if n_clicks is None or n_clicks == 0:
# Esto solo asegura que el display del precio inicial sea correcto
return "", f"Precio Actual del Activo (CEJ): ${precio_actual:.2f}"
# Simulamos que el precio puede haber cambiado (idealmente, esto vendría de un dcc.Interval)
# Para este ejemplo, solo usaremos el precio final de los datos simulados
costo_total = precio_actual * cantidad
mensaje = f"✅ ¡COMPRA EXITOSA! {cantidad} unidades de CEJ a ${precio_actual:.2f} cada una. Costo total: ${costo_total:.2f}"
# La salida del precio actual se mantiene:
precio_display = f"Precio Actual del Activo (CEJ): ${precio_actual:.2f}"
return mensaje, precio_display
# --- 5. Ejecución del Servidor ---
if __name__ == '__main__':
# El debug=True permite que los cambios en el código se reflejen automáticamente
app.run_server(debug=True)