# @title Web interactiva import gradio as gr import pandas as pd import yfinance as yf import matplotlib.pyplot as plt import plotly.graph_objects as go import numpy as np import os from datetime import date from processes import * # Función para obtener los datos y mostrarlos en el DataFrame de la UI import pandas as pd import yfinance as yf from datetime import date def generar_tabla(option_type, ticker_input, expiration_date, size_of_the_window): # Verificar si el ticker tiene opciones if not yf.Ticker(ticker_input).options: return pd.DataFrame() fechas_disponibles = pd.Series(yf.Ticker(ticker_input).options) all_tables = [] # Caso 1: expiración nula o vacía, buscar la más cercana if expiration_date is None or expiration_date == "": fecha_cercana = date.today().strftime('%Y-%m-%d') timedeltas = fechas_disponibles.apply(lambda x: pd.to_datetime(x) - pd.to_datetime(fecha_cercana)) expiration_date1 = fechas_disponibles[timedeltas.abs().idxmin()] df_final = data_option(ticker_input, expiration_date1, option_type, size_of_the_window) return df_final # Caso 2: todas las expiraciones elif expiration_date == "All": for fecha in fechas_disponibles: table = data_option(ticker_input, fecha, option_type, size_of_the_window) if not table.empty: table['dates'] = fecha all_tables.append(table) df_final = pd.concat(all_tables, ignore_index=True) return df_final # Caso 3: expiración específica else: timedeltas = fechas_disponibles.apply(lambda x: pd.to_datetime(x) - pd.to_datetime(expiration_date)) expiration_date1 = fechas_disponibles[timedeltas.abs().idxmin()] df_final = data_option(ticker_input, expiration_date1, option_type, size_of_the_window) return df_final def parse_opciones_table(df): """ Limpia y convierte un DataFrame extraído de una tabla decorada de opciones. Filtra filas válidas, convierte tipos y normaliza valores booleanos. """ # Filtrar filas que contienen símbolos válidos de opciones (ej. GOOGL251219P00165000) df_clean = df[df['contractSymbol'].str.contains(r'^[A-Z]{4,6}\d{6}[PC]\d{8}$', regex=True)].copy() # Convertir columnas relevantes a tipos numéricos df_clean['strike'] = pd.to_numeric(df_clean['strike'], errors='coerce') df_clean['volume'] = pd.to_numeric(df_clean['volume'], errors='coerce') df_clean['lastPrice'] = pd.to_numeric(df_clean['lastPrice'], errors='coerce') df_clean['impliedVolatility'] = df_clean['impliedVolatility'].astype(str).str.replace('%', '').astype(float) / 100 # Normalizar columna inTheMoney #df_clean['inTheMoney'] = df_clean['inTheMoney'].astype(str).str.upper().map({'VERDADERO': True, 'FALSO': False}) # Eliminar filas con valores nulos en columnas clave df_clean = df_clean.dropna(subset=['strike', 'volume', 'lastPrice', 'impliedVolatility']) return df_clean def plot_opciones(df_put, df_call, ticker_input, expiration_date): spot_price = yf.Ticker(ticker_input).info['regularMarketPrice'] puts_itm = df_put[df_put['strike'] > spot_price] puts_otm = df_put[df_put['strike'] <= spot_price] calls_itm = df_call[df_call['strike'] < spot_price] calls_otm = df_call[df_call['strike'] >= spot_price] fig1 = go.Figure() fig1.add_trace(go.Bar(x=puts_itm['strike'], y=puts_itm['volume'], name='PUT ITM', marker_color='green', width=1)) fig1.add_trace(go.Bar(x=puts_otm['strike'], y=puts_otm['volume'], name='PUT OTM', marker_color='red',width=1)) fig1.add_trace(go.Bar(x=calls_itm['strike'], y=calls_itm['volume'], name='CALL ITM', marker_color='blue',width=1)) fig1.add_trace(go.Bar(x=calls_otm['strike'], y=calls_otm['volume'], name='CALL OTM', marker_color='orange',width=1)) fig1.add_trace(go.Scatter(x=[spot_price], y=[0], mode='markers+text', marker=dict(color='black', size=10), text=[f'Spot: {spot_price}'], textposition='top center', name='Spot Price')) # Agrega la línea vertical discontinua fig1.add_trace(go.Scatter( x=[spot_price, spot_price], y=[0, max(max(puts_itm['volume']), max(puts_otm['volume']), max(calls_itm['volume']), max(calls_otm['volume'])) * 1.1], mode='lines', name=f'Spot: {spot_price}', marker=dict(color='black', size=10), line=dict(dash='dash'), showlegend=False )) fig1.update_layout(title=f"Didtribución del Volumen {ticker_input}", xaxis_title='Strike', yaxis_title='Volumen', barmode='stack', template='plotly_white', legend=dict( orientation="h", yanchor="top", y=-0.2, xanchor="center", x=0.5) ) fig2 = go.Figure() fig2.add_trace(go.Scatter( x=df_put['strike'], y=df_put['impliedVolatility'], mode='markers+lines', name='PUT', marker=dict(color='red') )) fig2.add_trace(go.Scatter( x=df_call['strike'], y=df_call['impliedVolatility'], mode='markers+lines', name='CALL', marker=dict(color='blue') )) # Agrega la línea vertical aquí fig2.add_vline(x=spot_price, line_width=2, line_dash="dot", line_color="black") fig2.update_layout( title=f"Volatilidad Implícita por Strike {ticker_input}", xaxis_title='Strike', yaxis_title='Volatilidad Implícita', template='plotly_white', legend=dict( orientation="h", yanchor="top", y=-0.2, xanchor="center", x=0.5) ) return fig1, fig2 def generar_puts(ticker_input, expiration_date, size_of_the_window): ticker_input = ticker_input puts = generar_tabla("put", ticker_input, expiration_date, size_of_the_window) clean_puts = parse_opciones_table(puts) return puts, clean_puts def generar_calls(ticker_input, expiration_date, size_of_the_window): ticker_input = ticker_input calls = generar_tabla("call", ticker_input, expiration_date, size_of_the_window) clean_calls = parse_opciones_table(calls) return calls, clean_calls def generar_todo(ticker_input, expiration_date, size_of_the_window): puts = generar_puts(ticker_input, expiration_date, size_of_the_window)[0] puts_clean = generar_puts(ticker_input, expiration_date, size_of_the_window)[1] calls = generar_calls(ticker_input, expiration_date, size_of_the_window)[0] calls_clean = generar_calls(ticker_input, expiration_date, size_of_the_window)[1] return puts, puts_clean, calls, calls_clean # Función para generar y devolver el archivo Excel def exportar_a_excel(option_type, ticker_input, expiration_date, size_of_the_window): df_final = generar_tabla(option_type, ticker_input, expiration_date, size_of_the_window) try: if not df_final.empty: nombre_archivo = f"{ticker_input}_opciones_{option_type}.xlsx" df_final.to_excel(nombre_archivo, index=False) return nombre_archivo, "Archivo Excel generado con éxito." else: return None, "No se pudieron generar los datos para exportar." except Exception as e: return None, f"Error al exportar: {e}" def exportar_a_excel_con_hojas(ticker_input, expiration_date, size_of_the_window): df_puts = generar_puts(ticker_input, expiration_date, size_of_the_window)[0] df_calls = generar_calls(ticker_input, expiration_date, size_of_the_window)[0] ticker = ticker_input.upper() nombre_archivo = f"{ticker}_opciones.xlsx" try: with pd.ExcelWriter(nombre_archivo) as writer: # Escribe el DataFrame de 'puts' en la primera hoja if not df_puts.empty: df_puts.to_excel(writer, sheet_name='Puts', index=False) # Escribe el DataFrame de 'calls' en la segunda hoja if not df_calls.empty: df_calls.to_excel(writer, sheet_name='Calls', index=False) return nombre_archivo, "Archivo Excel generado con éxito." except Exception as e: return None, f"Error al exportar: {e}" # ------------------------------- # Configuración de Gradio # ------------------------------- # --- Valores por defecto para inicializar la tabla --- initial_df_put = generar_tabla("put", "MSTR", '', 16) initial_df_call = generar_tabla("call", "MSTR", '', 16) with gr.Blocks(title="Análisis de Opciones") as demo: df_put_state = gr.State(value=parse_opciones_table(initial_df_put)) df_call_state = gr.State(value=parse_opciones_table(initial_df_call)) # Añade esta línea con la URL de tu imagen gr.Image("https://cdn.prod.website-files.com/67174dcb1d43b06a0eae317e/672bbbc7a2b88045e5661994_logo-afi.svg", show_label=False) gr.Markdown("# Analizador de Opciones") gr.Markdown("#### Herramienta para visualizar y exportar datos de opciones.") gr.Markdown("Introduzca el tipo de opción que desea consultar, indicando el Ticker y la Fecha de vencimiento. Si no se especifica una fecha de vencimiento, se mostrarán todas las disponibles.") with gr.Row(): ticker_input = gr.Textbox(label="Ticker (ej. GOOGL)", value="MSTR") expiration_date = gr.Textbox(label="Fecha de vencimiento (formato: aaaa-mm-dd)", value = "" ) size_of_the_window = gr.Slider(minimum=1, maximum=30, value=16, step=1, label="Tamaño de la ventana") btn_mostrar = gr.Button('Cargar Datos') with gr.Tab("Puts"): with gr.Row(): gr.Markdown("# Opciones Put") with gr.Row(): output_df_put = gr.DataFrame(label="Datos de Opciones put", value=initial_df_put) ''' ticker_input.change( fn = generar_puts, inputs=[ticker_input, expiration_date, size_of_the_window], outputs= [ output_df_put, df_put_state] ) ''' with gr.Tab("Calls"): with gr.Row(): gr.Markdown("# Opciones Call") #btn_mostrar2 = gr.Button("Mostrar datos") with gr.Row(): output_df_call = gr.DataFrame(label="Datos de Opciones call", value=initial_df_call) ''' ticker_input.change( fn = generar_calls, inputs=[ticker_input, expiration_date, size_of_the_window], outputs= [ output_df_call, df_call_state] ) ''' with gr.Tab("Plots"): btn_plot = gr.Button("Mostrar Plot") with gr.Row(): output_plot1 = gr.Plot(label="Distribución de Volumen de las opciones") output_plot2 = gr.Plot(label = 'Volatilidad Implícita por strike') btn_plot.click( fn=plot_opciones, inputs=[df_put_state, df_call_state, ticker_input, expiration_date], outputs=[output_plot1, output_plot2] ) with gr.Tab("Exportar"): gr.Markdown("# Exportación:") gr.Markdown("### Se generará un archivo excel con dos hojas: Calls y Puts, con las correspondientes tablas según los datos seleccionados") btn_descargar = gr.Button("Descargar en Excel") output_file = gr.File(label="Archivo Excel") output_msg = gr.Label() # Enlaza el botón "Mostrar datos" para actualizar los dataframes y luego para graficar. btn_descargar.click( fn=exportar_a_excel_con_hojas, inputs=[ticker_input, expiration_date, size_of_the_window], outputs=[output_file, output_msg] ) btn_mostrar.click( fn=generar_todo, inputs=[ticker_input, expiration_date, size_of_the_window], outputs= [ output_df_put, df_put_state, output_df_call, df_call_state] ) # Inicia la aplicación demo.launch(share=True)