Quant0/options
0
1# @title Web interactiva2import gradio as gr3import pandas as pd4import yfinance as yf5import matplotlib.pyplot as plt6import plotly.graph_objects as go7import numpy as np8import os9from datetime import date10 11from processes import *12# Función para obtener los datos y mostrarlos en el DataFrame de la UI13import pandas as pd14import yfinance as yf15from datetime import date16 17def generar_tabla(option_type, ticker_input, expiration_date, size_of_the_window):18 # Verificar si el ticker tiene opciones19 if not yf.Ticker(ticker_input).options:20 return pd.DataFrame()21 22 fechas_disponibles = pd.Series(yf.Ticker(ticker_input).options)23 all_tables = []24 25 # Caso 1: expiración nula o vacía, buscar la más cercana26 if expiration_date is None or expiration_date == "":27 fecha_cercana = date.today().strftime('%Y-%m-%d')28 timedeltas = fechas_disponibles.apply(lambda x: pd.to_datetime(x) - pd.to_datetime(fecha_cercana))29 expiration_date1 = fechas_disponibles[timedeltas.abs().idxmin()]30 df_final = data_option(ticker_input, expiration_date1, option_type, size_of_the_window)31 return df_final32 33 # Caso 2: todas las expiraciones34 elif expiration_date == "All":35 for fecha in fechas_disponibles:36 table = data_option(ticker_input, fecha, option_type, size_of_the_window)37 if not table.empty:38 table['dates'] = fecha39 all_tables.append(table)40 df_final = pd.concat(all_tables, ignore_index=True)41 return df_final42 43 # Caso 3: expiración específica44 else:45 timedeltas = fechas_disponibles.apply(lambda x: pd.to_datetime(x) - pd.to_datetime(expiration_date))46 expiration_date1 = fechas_disponibles[timedeltas.abs().idxmin()]47 df_final = data_option(ticker_input, expiration_date1, option_type, size_of_the_window)48 return df_final49 50def parse_opciones_table(df):51 """52 Limpia y convierte un DataFrame extraído de una tabla decorada de opciones.53 Filtra filas válidas, convierte tipos y normaliza valores booleanos.54 """55 # Filtrar filas que contienen símbolos válidos de opciones (ej. GOOGL251219P00165000)56 df_clean = df[df['contractSymbol'].str.contains(r'^[A-Z]{4,6}\d{6}[PC]\d{8}$', regex=True)].copy()57 58 # Convertir columnas relevantes a tipos numéricos59 df_clean['strike'] = pd.to_numeric(df_clean['strike'], errors='coerce')60 df_clean['volume'] = pd.to_numeric(df_clean['volume'], errors='coerce')61 df_clean['lastPrice'] = pd.to_numeric(df_clean['lastPrice'], errors='coerce')62 df_clean['impliedVolatility'] = df_clean['impliedVolatility'].astype(str).str.replace('%', '').astype(float) / 10063 64 # Normalizar columna inTheMoney65 #df_clean['inTheMoney'] = df_clean['inTheMoney'].astype(str).str.upper().map({'VERDADERO': True, 'FALSO': False})66 67 # Eliminar filas con valores nulos en columnas clave68 df_clean = df_clean.dropna(subset=['strike', 'volume', 'lastPrice', 'impliedVolatility'])69 70 return df_clean71 72def plot_opciones(df_put, df_call, ticker_input, expiration_date):73 74 spot_price = yf.Ticker(ticker_input).info['regularMarketPrice']75 puts_itm = df_put[df_put['strike'] > spot_price]76 puts_otm = df_put[df_put['strike'] <= spot_price]77 calls_itm = df_call[df_call['strike'] < spot_price]78 calls_otm = df_call[df_call['strike'] >= spot_price]79 80 fig1 = go.Figure()81 fig1.add_trace(go.Bar(x=puts_itm['strike'], y=puts_itm['volume'], name='PUT ITM', marker_color='green', width=1))82 fig1.add_trace(go.Bar(x=puts_otm['strike'], y=puts_otm['volume'], name='PUT OTM', marker_color='red',width=1))83 fig1.add_trace(go.Bar(x=calls_itm['strike'], y=calls_itm['volume'], name='CALL ITM', marker_color='blue',width=1))84 fig1.add_trace(go.Bar(x=calls_otm['strike'], y=calls_otm['volume'], name='CALL OTM', marker_color='orange',width=1))85 fig1.add_trace(go.Scatter(x=[spot_price], y=[0], mode='markers+text',86 marker=dict(color='black', size=10),87 text=[f'Spot: {spot_price}'],88 textposition='top center',89 name='Spot Price'))90 # Agrega la línea vertical discontinua91 fig1.add_trace(go.Scatter(92 x=[spot_price, spot_price],93 y=[0, max(max(puts_itm['volume']), max(puts_otm['volume']), max(calls_itm['volume']), max(calls_otm['volume'])) * 1.1],94 mode='lines',95 name=f'Spot: {spot_price}',96 marker=dict(color='black', size=10),97 line=dict(dash='dash'),98 showlegend=False99 ))100 101 102 fig1.update_layout(title=f"Didtribución del Volumen {ticker_input}",103 xaxis_title='Strike',104 yaxis_title='Volumen',105 barmode='stack',106 template='plotly_white',107 legend=dict(108 orientation="h",109 yanchor="top",110 y=-0.2,111 xanchor="center",112 x=0.5)113 )114 115 116 fig2 = go.Figure()117 118 fig2.add_trace(go.Scatter(119 x=df_put['strike'],120 y=df_put['impliedVolatility'],121 mode='markers+lines',122 name='PUT',123 marker=dict(color='red')124 125 ))126 127 fig2.add_trace(go.Scatter(128 x=df_call['strike'],129 y=df_call['impliedVolatility'],130 mode='markers+lines',131 name='CALL',132 marker=dict(color='blue')133 134 ))135 136 # Agrega la línea vertical aquí137 fig2.add_vline(x=spot_price, line_width=2, line_dash="dot", line_color="black")138 139 fig2.update_layout(140 title=f"Volatilidad Implícita por Strike {ticker_input}",141 xaxis_title='Strike',142 yaxis_title='Volatilidad Implícita',143 template='plotly_white',144 legend=dict(145 orientation="h",146 yanchor="top",147 y=-0.2,148 xanchor="center",149 x=0.5)150 151 )152 153 return fig1, fig2154 155 156def generar_puts(ticker_input, expiration_date, size_of_the_window):157 ticker_input = ticker_input158 puts = generar_tabla("put", ticker_input, expiration_date, size_of_the_window)159 clean_puts = parse_opciones_table(puts)160 return puts, clean_puts161 162def generar_calls(ticker_input, expiration_date, size_of_the_window):163 ticker_input = ticker_input164 calls = generar_tabla("call", ticker_input, expiration_date, size_of_the_window)165 clean_calls = parse_opciones_table(calls)166 return calls, clean_calls167 168def generar_todo(ticker_input, expiration_date, size_of_the_window):169 puts = generar_puts(ticker_input, expiration_date, size_of_the_window)[0]170 puts_clean = generar_puts(ticker_input, expiration_date, size_of_the_window)[1]171 calls = generar_calls(ticker_input, expiration_date, size_of_the_window)[0]172 calls_clean = generar_calls(ticker_input, expiration_date, size_of_the_window)[1]173 return puts, puts_clean, calls, calls_clean174 175# Función para generar y devolver el archivo Excel176def exportar_a_excel(option_type, ticker_input, expiration_date, size_of_the_window):177 178 df_final = generar_tabla(option_type, ticker_input, expiration_date, size_of_the_window)179 try:180 if not df_final.empty:181 nombre_archivo = f"{ticker_input}_opciones_{option_type}.xlsx"182 df_final.to_excel(nombre_archivo, index=False)183 return nombre_archivo, "Archivo Excel generado con éxito."184 else:185 return None, "No se pudieron generar los datos para exportar."186 except Exception as e:187 return None, f"Error al exportar: {e}"188 189 190 191 192 193 194def exportar_a_excel_con_hojas(ticker_input, expiration_date, size_of_the_window):195 196 df_puts = generar_puts(ticker_input, expiration_date, size_of_the_window)[0]197 df_calls = generar_calls(ticker_input, expiration_date, size_of_the_window)[0]198 ticker = ticker_input.upper()199 nombre_archivo = f"{ticker}_opciones.xlsx"200 try:201 with pd.ExcelWriter(nombre_archivo) as writer:202 # Escribe el DataFrame de 'puts' en la primera hoja203 if not df_puts.empty:204 df_puts.to_excel(writer, sheet_name='Puts', index=False)205 206 # Escribe el DataFrame de 'calls' en la segunda hoja207 if not df_calls.empty:208 df_calls.to_excel(writer, sheet_name='Calls', index=False)209 210 return nombre_archivo, "Archivo Excel generado con éxito."211 except Exception as e:212 return None, f"Error al exportar: {e}"213 214 215 216 217 218# -------------------------------219# Configuración de Gradio220# -------------------------------221 222 223# --- Valores por defecto para inicializar la tabla ---224initial_df_put = generar_tabla("put", "MSTR", '', 16)225initial_df_call = generar_tabla("call", "MSTR", '', 16)226 227 228with gr.Blocks(title="Análisis de Opciones") as demo:229 230 df_put_state = gr.State(value=parse_opciones_table(initial_df_put))231 df_call_state = gr.State(value=parse_opciones_table(initial_df_call))232 233 # Añade esta línea con la URL de tu imagen234 gr.Image("https://cdn.prod.website-files.com/67174dcb1d43b06a0eae317e/672bbbc7a2b88045e5661994_logo-afi.svg", show_label=False)235 236 gr.Markdown("# Analizador de Opciones")237 gr.Markdown("#### Herramienta para visualizar y exportar datos de opciones.")238 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.")239 with gr.Row():240 ticker_input = gr.Textbox(label="Ticker (ej. GOOGL)", value="MSTR")241 expiration_date = gr.Textbox(label="Fecha de vencimiento (formato: aaaa-mm-dd)", value = "" )242 size_of_the_window = gr.Slider(minimum=1, maximum=30, value=16, step=1, label="Tamaño de la ventana")243 btn_mostrar = gr.Button('Cargar Datos')244 245 246 247 with gr.Tab("Puts"):248 249 with gr.Row():250 gr.Markdown("# Opciones Put")251 252 with gr.Row():253 254 output_df_put = gr.DataFrame(label="Datos de Opciones put", value=initial_df_put) 255 256 ''' 257 ticker_input.change(258 fn = generar_puts,259 inputs=[ticker_input, expiration_date, size_of_the_window],260 outputs= [ output_df_put, df_put_state]261 )262 '''263 264 with gr.Tab("Calls"):265 266 with gr.Row():267 gr.Markdown("# Opciones Call")268 #btn_mostrar2 = gr.Button("Mostrar datos")269 270 with gr.Row():271 272 output_df_call = gr.DataFrame(label="Datos de Opciones call", value=initial_df_call)273 274 275 ''' 276 ticker_input.change(277 fn = generar_calls,278 inputs=[ticker_input, expiration_date, size_of_the_window],279 outputs= [ output_df_call, df_call_state]280 )281 '''282 with gr.Tab("Plots"):283 284 btn_plot = gr.Button("Mostrar Plot")285 286 with gr.Row():287 output_plot1 = gr.Plot(label="Distribución de Volumen de las opciones")288 output_plot2 = gr.Plot(label = 'Volatilidad Implícita por strike')289 290 btn_plot.click(291 fn=plot_opciones,292 inputs=[df_put_state, df_call_state, ticker_input, expiration_date],293 outputs=[output_plot1, output_plot2]294 ) 295 296 with gr.Tab("Exportar"):297 gr.Markdown("# Exportación:")298 gr.Markdown("### Se generará un archivo excel con dos hojas: Calls y Puts, con las correspondientes tablas según los datos seleccionados")299 btn_descargar = gr.Button("Descargar en Excel")300 output_file = gr.File(label="Archivo Excel")301 output_msg = gr.Label() 302 303 # Enlaza el botón "Mostrar datos" para actualizar los dataframes y luego para graficar.304 305 btn_descargar.click(306 fn=exportar_a_excel_con_hojas,307 inputs=[ticker_input, expiration_date, size_of_the_window],308 outputs=[output_file, output_msg]309 ) 310 btn_mostrar.click(311 fn=generar_todo,312 inputs=[ticker_input, expiration_date, size_of_the_window],313 outputs= [ output_df_put, df_put_state, output_df_call, df_call_state]314 ) 315 316# Inicia la aplicación317demo.launch(share=True)