from flask import Flask, request, jsonify
from flask import Flask, render_template, request, jsonify
import pandas as pd
import json
import glob
import os
import time
import pprint
from datetime import datetime
from collections import deque

PATH_UBUNTU = "/var/www/html/flask_project/"
pd.set_option('display.max_rows', None)  # Muestra todas las filas
# pd.set_option('display.max_columns', None)  # Muestra todas las columnas

app = Flask(__name__)

# Ruta para renderizar la página principal


@app.route('/')
def index():
    return render_template('backtesting.html')


# Modo Debug: True para ver mensajes en consola, False para ocultarlos
DEBUG_MODE = False


def debug_print(*args, **kwargs):
    """Imprime mensajes solo si DEBUG_MODE está activado."""
    if DEBUG_MODE:
        print(*args, **kwargs)

from concurrent.futures import ProcessPoolExecutor

def load_csv(file_info):
    """Carga un archivo CSV y devuelve su contenido si es válido."""
    file_date, file_path = file_info
    try:
        return file_date, pd.read_csv(file_path)
    except Exception as e:
        print(f"⚠ Error cargando archivo {file_path}: {e}")
        return None  # Retornar None si hay un error al leer el archivo
    
@app.route('/process_form', methods=['POST'])
def process_form():
    data = request.get_json()

    # Extraer información desde el JSON
    symbol = data.get('symbol')
    if symbol in ['SPX', 'XSP', 'RUT']:
        symbol = '$' + symbol

    fecha_desde = data.get('fechaDesde')
    fecha_hasta = data.get('fechaHasta')
    horario = data.get('horario') + ":00"
    call_delta = data['deltas']['call']
    put_delta = data['deltas']['put']
    takeProfit = data.get('takeProfit')
    stopLoss = data.get('stopLoss')
    unit = data.get('unit')  
    call_spread = data['spreads']['call']
    put_spread = data['spreads']['put']

    # Convertir fechas a objetos datetime
    fecha_desde_dt = datetime.strptime(fecha_desde, '%Y-%m-%d')
    fecha_hasta_dt = datetime.strptime(fecha_hasta, '%Y-%m-%d')

    # Directorio donde están los archivos CSV
    directory_path = '/var/www/html/flask_project/chains'

    # Construir el patrón de búsqueda
    pattern = f"{directory_path}/optionChain_{symbol}_*.csv"

    files_with_dates = []

    for file_path in glob.glob(pattern):
        base_name = os.path.basename(file_path)
        parts = base_name.replace('.csv', '').split('_')
        
        # Verificar si el nombre tiene formato esperado
        if len(parts) < 3:
            print(f"⚠ Advertencia: Nombre inesperado en {base_name}, archivo ignorado.")
            continue

        file_date_str = parts[-1]

        try:
            file_date_dt = datetime.strptime(file_date_str, '%Y-%m-%d')
        except ValueError:
            print(f"⚠ Error: Fecha inválida en {base_name}, archivo ignorado.")
            continue

        # Agregar solo si la fecha está en el rango
        if fecha_desde_dt <= file_date_dt <= fecha_hasta_dt:
            files_with_dates.append((file_date_dt, file_path))

    # Ordenar archivos por fecha
    files_with_dates.sort()

    # Si no hay archivos válidos
    if not files_with_dates:
        return jsonify({
            'status': 'error',
            'message': 'No se encontraron archivos en el rango seleccionado.',
            'data': []
        })

    # 📌 Cargar archivos CSV en paralelo
    with ProcessPoolExecutor() as executor:
        grouped = list(filter(None, executor.map(load_csv, files_with_dates)))  # Filtrar errores

    # 📌 Procesar cada día en secuencia con process_day()
    results = [process_day(group, horario, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit)
               for _, group in grouped if process_day(group, horario, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit) is not None]

    return jsonify({
        'status': 'success' if results else 'error',
        'data': results,
        'message': 'Datos procesados correctamente' if results else 'No se encontraron datos procesables.'
    })


def process_day(day_df, horario, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit):
    """Procesa un solo día de datos y devuelve el resultado si es válido."""
    result = find_next_closest_time_rows(day_df, horario, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit)
    return result if result else None


import numpy as np

import numpy as np

def find_next_closest_time_rows(df, target_time_str, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit):
    """Encuentra las filas con el timestamp más cercano o posterior al tiempo objetivo, selecciona los strikes del Iron Condor y calcula el crédito inicial."""

    # 📌 Convertir la columna 'timestamp' a datetime
    df['Time'] = pd.to_datetime(df['timestamp'])
    df['only_time'] = df['Time'].dt.time

    # 📌 Convertir target_time a formato de hora
    target_time = datetime.strptime(target_time_str, '%H:%M:%S').time()

    # 📌 Filtrar SOLO registros dentro del horario de mercado (9:30 - 16:00)
    market_open = datetime.strptime("09:30:00", "%H:%M:%S").time()
    market_close = datetime.strptime("15:59:59", "%H:%M:%S").time()
    
    df = df[(df['only_time'] >= market_open) & (df['only_time'] <= market_close)].copy()

    if df.empty:
        debug_print(f"⚠ No hay datos en horario de mercado (09:30 - 16:00)")
        return None

    # 📌 Filtrar los timestamps iguales o posteriores al target_time
    future_times = df[df['only_time'] >= target_time].copy()

    if future_times.empty:
        debug_print(f"⚠ No hay timestamps iguales o posteriores a {target_time_str} dentro del horario de mercado.")
        return None

    # 📌 Ordenar por timestamp y tomar el primer timestamp disponible
    future_times = future_times.sort_values(by='Time', ascending=True)
    selected_timestamp = future_times.iloc[0]['timestamp']
    
    # 📌 Filtrar todas las filas que coincidan con ese timestamp
    closest_rows = df[df['timestamp'] == selected_timestamp].copy()

    debug_print("📌 Timestamp más cercano encontrado:")
    debug_print(closest_rows[['timestamp']].drop_duplicates())

    # 📌 Encontrar los strikes más cercanos a los deltas deseados
    closest_rows['delta_call_diff'] = np.abs(closest_rows['delta_call'] - float(call_delta))
    closest_rows['delta_put_diff'] = np.abs(closest_rows['delta_put'] - float(put_delta))

    sell_call = closest_rows.loc[closest_rows['delta_call_diff'].idxmin(), 'strike']
    sell_put = closest_rows.loc[closest_rows['delta_put_diff'].idxmin(), 'strike']

    # 📌 Determinar los strikes de compra en base a call_spread y put_spread
    buy_call = sell_call + float(call_spread)
    buy_put = sell_put - float(put_spread)

    # 📌 Filtrar solo las filas con los strikes necesarios
    required_strikes = [sell_call, buy_call, sell_put, buy_put]
    filtered_df = closest_rows[closest_rows['strike'].isin(required_strikes)]

    # 📌 Debugging: Mostrar los strikes disponibles después del filtrado
    available_strikes = filtered_df['strike'].unique()
    missing_strikes = [strike for strike in required_strikes if strike not in available_strikes]

    if missing_strikes:
        debug_print(f"⚠ Strikes faltantes en el DataFrame: {missing_strikes}")
        debug_print(f"📌 Strikes disponibles en el df: {available_strikes}")
        debug_print("❌ Día descartado debido a datos insuficientes para el Iron Condor.")
        return None

    # 📌 Debugging: Mostrar los strikes del Iron Condor
    debug_print("📌 Strikes del Iron Condor:")
    debug_print(f"📌 Sell Call: {sell_call}")
    debug_print(f"📌 Buy Call: {buy_call}")
    debug_print(f"📌 Sell Put: {sell_put}")
    debug_print(f"📌 Buy Put: {buy_put}")

    # 📌 Calcular el crédito inicial
    credit = calculate_iron_condor_credit(filtered_df, sell_call, buy_call, sell_put, buy_put)

    if credit is None:
        debug_print(f"🚨 Día descartado: {selected_timestamp[:10]} - No se pudo calcular el crédito.")
        return None

    # 📌 Debugging: Mostrar crédito inicial
    debug_print(f"💰 Crédito Inicial del Iron Condor: {credit:.2f}")

    # 📌 Evaluar el desempeño del Iron Condor
    resultado, final_credit, occur_time = evaluate_iron_condor_performance(
        df, sell_call, buy_call, sell_put, buy_put, credit, takeProfit, stopLoss, selected_timestamp
    )

    debug_print(f"📌 Resultado final del Iron Condor: {resultado}")

    return {
        "Time": selected_timestamp,
        "Outcome": resultado,
        "Initial Credit": credit,
        "Credit at Occurrence": final_credit,
        "Occur Time": occur_time,
        "Stop Loss": stopLoss,
        "Take Profit": takeProfit
    }




def calculate_iron_condor_credit(df, sell_call, buy_call, sell_put, buy_put):
    """Calcula el crédito inicial del Iron Condor, validando que cada strike esté presente."""

    # Convertir a float para evitar problemas de comparación
    sell_call, buy_call, sell_put, buy_put = map(float, [sell_call, buy_call, sell_put, buy_put])

    # Asegurar que la columna 'strike' es float
    df['strike'] = df['strike'].astype(float)

    # Verificar si cada strike está presente en el DataFrame
    missing_strikes = [strike for strike in [sell_call, buy_call, sell_put, buy_put] if strike not in df['strike'].values]

    if missing_strikes:
        debug_print(f"⚠ Strikes faltantes en el DataFrame: {missing_strikes}")
        debug_print(f"📌 Strikes disponibles en el df: {df['strike'].unique()}")
        debug_print("❌ Día descartado debido a datos insuficientes para el Iron Condor.")
        return None  # ❌ Se retorna None para indicar que no se puede procesar

    try:
        # Obtener las filas correspondientes a cada strike
        sell_call_row = df[df['strike'] == sell_call].iloc[0]
        buy_call_row = df[df['strike'] == buy_call].iloc[0]
        sell_put_row = df[df['strike'] == sell_put].iloc[0]
        buy_put_row = df[df['strike'] == buy_put].iloc[0]

        # Calcular el precio promedio (Bid + Ask) / 2 para cada opción
        sell_call_price = (sell_call_row['bid_call'] + sell_call_row['ask_call']) / 2
        buy_call_price = (buy_call_row['bid_call'] + buy_call_row['ask_call']) / 2
        sell_put_price = (sell_put_row['bid_put'] + sell_put_row['ask_put']) / 2
        buy_put_price = (buy_put_row['bid_put'] + buy_put_row['ask_put']) / 2

        # Calcular el crédito inicial del Iron Condor
        credit = (sell_call_price - buy_call_price) + (sell_put_price - buy_put_price)

        debug_print("\n🔹 Valores de precios:")
        debug_print(f"📌 Sell Call ({sell_call}): {sell_call_price:.2f}")
        debug_print(f"📌 Buy Call ({buy_call}): {buy_call_price:.2f}")
        debug_print(f"📌 Sell Put ({sell_put}): {sell_put_price:.2f}")
        debug_print(f"📌 Buy Put ({buy_put}): {buy_put_price:.2f}")

        return credit

    except IndexError as e:
        debug_print(f"⚠ Error al seleccionar filas del DataFrame: {e}")
        return None




def evaluate_iron_condor_performance(df, sell_call, buy_call, sell_put, buy_put, initial_credit, take_profit, stop_loss, opening_timestamp):
    """Evalúa el desempeño del Iron Condor de manera optimizada."""

    take_profit = float(take_profit)  # TP en puntos
    stop_loss = float(stop_loss)  # SL en puntos
    initial_credit = float(initial_credit) * 100  # Multiplicar Crédito Inicial por 100

    debug_print("\n📊 Evaluando el desempeño del Iron Condor...")

    # Convertir y ordenar timestamps
    df["timestamp"] = pd.to_datetime(df["timestamp"])
    df = df[df["timestamp"] >= opening_timestamp].sort_values("timestamp")

    # Filtrar solo los strikes relevantes en una sola operación
    strikes_needed = {sell_call, buy_call, sell_put, buy_put}
    df = df[df["strike"].isin(strikes_needed)]

    if df.empty:
        debug_print("⚠ No hay datos suficientes después del timestamp de apertura.")
        return "Neutro", initial_credit, opening_timestamp.strftime('%H:%M:%S')

    # Convertir df en un diccionario de acceso rápido
    df_grouped = df.groupby("timestamp")

    # Calcular el mid-price para cada strike
    df["mid_call"] = (df["bid_call"] + df["ask_call"]) / 2
    df["mid_put"] = (df["bid_put"] + df["ask_put"]) / 2

    # Crear DataFrame pivotado para acceso rápido
    pivot_df = df.pivot(index="timestamp", columns="strike", values=["mid_call", "mid_put"])

    # Aplicar rolling window para suavizar precios (ventana de 3)
    pivot_df = pivot_df.rolling(window=3, min_periods=1).mean()

    # Calcular el crédito inicial del Iron Condor en cada timestamp
    pivot_df["credit"] = (
        (pivot_df["mid_call", sell_call] - pivot_df["mid_call", buy_call]) +
        (pivot_df["mid_put", sell_put] - pivot_df["mid_put", buy_put])
    ) * 100  # Multiplicar por 100

    # Iterar sobre los timestamps y evaluar TP/SL
    for timestamp, row in pivot_df.iterrows():
        # Asegurar que current_credit es un escalar, no una Serie
        current_credit = row["credit"]
        if isinstance(current_credit, pd.Series):
            current_credit = current_credit.iloc[0]

        debug_print(f"⏳ {timestamp} | Crédito: {current_credit:.2f} | P&L: {(initial_credit - current_credit):.2f}")

        if current_credit <= initial_credit - take_profit:  # Take Profit alcanzado
            debug_print(f"✅ Take Profit alcanzado en {timestamp} con crédito de {current_credit:.2f}")
            return "Ganancia", current_credit, timestamp.strftime('%H:%M:%S')

        if current_credit >= initial_credit + stop_loss:  # Stop Loss alcanzado
            debug_print(f"❌ Stop Loss alcanzado en {timestamp} con crédito de {current_credit:.2f}")
            return "Pérdida", current_credit, timestamp.strftime('%H:%M:%S')

    debug_print("📌 Iron Condor llegó al final sin alcanzar TP ni SL")
    return "Neutro", current_credit, timestamp.strftime('%H:%M:%S')


# def evaluate_iron_condor_performance(df, sell_call, buy_call, sell_put, buy_put, initial_credit, take_profit, stop_loss, opening_timestamp):
#     """Evalúa el desempeño del Iron Condor revisando si toca el Take Profit o Stop Loss desde su apertura en adelante, calculando correctamente el mid price y mostrando cada paso detallado."""

#     take_profit = float(take_profit)  # TP en puntos
#     stop_loss = float(stop_loss)  # SL en puntos
#     initial_credit = float(initial_credit) * 100  # Multiplicar Crédito Inicial por 100

#     print("\n📊 Evolución del crédito del Iron Condor desde su apertura:")

#     # Convertir 'timestamp' a datetime en caso de que no lo sea
#     df["timestamp"] = pd.to_datetime(df["timestamp"])

#     # Ordenar el dataframe por timestamp antes de filtrar
#     df = df.sort_values(by="timestamp")

#     # Filtrar solo timestamps posteriores o iguales al de apertura
#     df = df[df["timestamp"] >= opening_timestamp]

#     for timestamp in df["timestamp"].unique():  # Iterar por cada timestamp posterior a la apertura
#         df_current = df[df["timestamp"] == timestamp]  # Filtrar las filas del timestamp actual

#         # Obtener precios de los strikes
#         sell_call_row = df_current[df_current["strike"] == sell_call]
#         buy_call_row = df_current[df_current["strike"] == buy_call]
#         sell_put_row = df_current[df_current["strike"] == sell_put]
#         buy_put_row = df_current[df_current["strike"] == buy_put]

#         # Verificar si tenemos todos los datos para calcular el crédito
#         if sell_call_row.empty or buy_call_row.empty or sell_put_row.empty or buy_put_row.empty:
#             print(f"⚠ Datos faltantes en {timestamp}, saltando...")
#             continue  # Si falta algún dato, pasamos al siguiente timestamp

#         # Extraer precios usando el mid price
#         try:
#             sell_call_bid = float(sell_call_row["bid_call"].values[0])
#             sell_call_ask = float(sell_call_row["ask_call"].values[0])
#             sell_call_mid = (sell_call_bid + sell_call_ask) / 2

#             buy_call_bid = float(buy_call_row["bid_call"].values[0])
#             buy_call_ask = float(buy_call_row["ask_call"].values[0])
#             buy_call_mid = (buy_call_bid + buy_call_ask) / 2

#             sell_put_bid = float(sell_put_row["bid_put"].values[0])
#             sell_put_ask = float(sell_put_row["ask_put"].values[0])
#             sell_put_mid = (sell_put_bid + sell_put_ask) / 2

#             buy_put_bid = float(buy_put_row["bid_put"].values[0])
#             buy_put_ask = float(buy_put_row["ask_put"].values[0])
#             buy_put_mid = (buy_put_bid + buy_put_ask) / 2
#         except (IndexError, ValueError) as e:
#             print(f"⚠ Error obteniendo precios en {timestamp}: {e}, saltando...")
#             continue

#         # Calcular el crédito actual del Iron Condor (multiplicado por 100)
#         current_credit = ((sell_call_mid - buy_call_mid) + (sell_put_mid - buy_put_mid)) * 100

#         # 📌 Mostrar todos los valores de cada componente para revisar
#         print(f"⏳ {timestamp} | "
#               f"SC: {sell_call:.1f} [{sell_call_bid:.2f}/{sell_call_ask:.2f}] -> {sell_call_mid:.2f} | "
#               f"BC: {buy_call:.1f} [{buy_call_bid:.2f}/{buy_call_ask:.2f}] -> {buy_call_mid:.2f} | "
#               f"SP: {sell_put:.1f} [{sell_put_bid:.2f}/{sell_put_ask:.2f}] -> {sell_put_mid:.2f} | "
#               f"BP: {buy_put:.1f} [{buy_put_bid:.2f}/{buy_put_ask:.2f}] -> {buy_put_mid:.2f} | "
#               f"Crédito: {current_credit:.2f} | P&L: {(initial_credit - current_credit):.2f}")

#         # Comparar con Take Profit y Stop Loss
#         if current_credit <= initial_credit - take_profit:   # Take Profit alcanzado
#             print(f"✅ Take Profit alcanzado en {timestamp} con un crédito de {current_credit:.2f}")
#             return "Ganancia"

#         if current_credit >= initial_credit + stop_loss:   # Stop Loss alcanzado
#             print(f"❌ Stop Loss alcanzado en {timestamp} con un crédito de {current_credit:.2f}")
#             return "Pérdida"

#     print("📌 El Iron Condor llegó al final sin alcanzar Take Profit ni Stop Loss")
#     return "Neutro"


# # Simulando la definición de la función dentro de un script de prueba
# def find_next_closest_time_rows(df, target_time_str, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit):
#     """Encuentra las filas con el horario más cercano y posterior al especificado."""
#     # Convertir la columna de horarios a datetime
#     df['Time'] = pd.to_datetime(df['timestamp'], format='%Y-%m-%d %H:%M:%S')
#     target_time = datetime.strptime(target_time_str, '%H:%M:%S').time()  # solo la hora

#     # Filtrar solo los tiempos que son iguales o después del tiempo objetivo
#     df['only_time'] = df['Time'].dt.time  # extraer solo la hora de la columna datetime
#     future_times = df[df['only_time'] >= target_time].copy()  # Crear una copia para evitar SettingWithCopyWarning

#     if future_times.empty:
#         return pd.DataFrame()  # Si no hay tiempos futuros, retornar dataframe vacío

#     # Calcular la diferencia de tiempo en segundos solo para tiempos futuros
#     future_times['time_diff'] = future_times['only_time'].apply(
#         lambda x: abs((datetime.combine(datetime.min, x) - datetime.combine(datetime.min, target_time)).total_seconds())
#     )
#     min_diff = future_times['time_diff'].min()

#     # Seleccionar las filas con la menor diferencia de tiempo
#     closest_rows = future_times[future_times['time_diff'] == min_diff]

#     # Convertir deltas recibidos a float
#     call_delta = float(call_delta)
#     put_delta = float(put_delta)
#     call_spread = float(call_spread)
#     put_spread = float(put_spread)

#     # Antes de modificar los valores, asegúrate de que `closest_rows` es una copia explícita
#     closest_rows = future_times[future_times['time_diff'] == min_diff].copy()

#     # Calcular la diferencia absoluta de deltas respecto a los valores deseados para calls y puts
#     closest_rows['delta_call_diff'] = abs(closest_rows['delta_call'] - call_delta)
#     closest_rows['delta_put_diff'] = abs(closest_rows['delta_put'] - put_delta)


#     # Encontrar el índice de la fila con la menor diferencia de delta para calls y puts
#     idx_min_call_delta = closest_rows['delta_call_diff'].idxmin()
#     idx_min_put_delta = closest_rows['delta_put_diff'].idxmin()

#       # Retornar las filas con los deltas más cercanos para calls y puts
#     closest_call_row = closest_rows.loc[[idx_min_call_delta]]
#     closest_put_row = closest_rows.loc[[idx_min_put_delta]]

#     # Calcular los strikes de los spreads
#     spread_call_strike = closest_call_row['strike'].values[0] + call_spread
#     spread_put_strike = closest_put_row['strike'].values[0] - put_spread

#     # Encontrar las filas para los strikes de los spreads
#     spread_call_row = closest_rows[closest_rows['strike'] == spread_call_strike]
#     spread_put_row = closest_rows[closest_rows['strike'] == spread_put_strike]

#     # Concatenar las filas de delta y spread en un solo DataFrame
#     result = pd.concat([spread_call_row, closest_call_row, closest_put_row,  spread_put_row])

#     sell_call = closest_call_row['strike'].values[0]
#     buy_call = spread_call_row['strike'].values[0]
#     sell_put = closest_put_row['strike'].values[0]
#     buy_put = spread_put_row['strike'].values[0]

#     print(sell_call, buy_call, sell_put, buy_put)
#     # Calcular el crédito obtenido del Iron Condor
#     credit = (
#         ((closest_call_row['bid_call'].values[0] + closest_call_row['ask_call'].values[0]) / 2 -
#          (spread_call_row['bid_call'].values[0] + spread_call_row['ask_call'].values[0]) / 2) +
#         ((closest_put_row['bid_put'].values[0] + closest_put_row['ask_put'].values[0]) / 2 -
#          (spread_put_row['bid_put'].values[0] + spread_put_row['ask_put'].values[0]) / 2)
#     )

#     goodOrBad = calculate_iron_condor_credits(target_time_str, sell_call, buy_call, sell_put, buy_put, df, takeProfit, stopLoss, unit)
#     get_all_credits(target_time_str, sell_call, buy_call, sell_put, buy_put, df)
#     return goodOrBad
#     # return result[['timestamp', 'underlying_price', 'strike', 'bid_call', 'ask_call', 'bid_put', 'ask_put', 'delta_call', 'delta_put']]


# def calculate_iron_condor_credits(time_start, sell_call_strike, buy_call_strike, sell_put_strike, buy_put_strike, df, take_profit, stop_loss, unit):
#     # Ensure timestamp is in datetime format
#     if not pd.api.types.is_datetime64_any_dtype(df['timestamp']):
#         df['timestamp'] = pd.to_datetime(df['timestamp'])

#     # Extraer la fecha de la primera fila de la columna 'timestamp'
#     first_row_date = df['timestamp'].iloc[0].date().strftime('%m/%d/%Y')

#     # Filter only the relevant rows based on time and strike
#     df = df[df['timestamp'].dt.time >= pd.to_datetime(time_start).time()]
#     df = df[df['strike'].isin([sell_call_strike, buy_call_strike, sell_put_strike, buy_put_strike])]

#     # Calculate the mean prices for calls and puts directly into the DataFrame
#     df['mean_price'] = ((df['bid_call'] + df['ask_call']) / 2 * df['strike'].isin([sell_call_strike, buy_call_strike])
#                         + (df['bid_put'] + df['ask_put']) / 2 * df['strike'].isin([sell_put_strike, buy_put_strike])) * 100

#     # Create a flag to differentiate sell and buy
#     df['sell_buy'] = df['strike'].apply(lambda x: 'sell' if x in [sell_call_strike, sell_put_strike] else 'buy')

#     # Pivot the table to get sell and buy on separate columns, and compute the credit
#     pivot = df.pivot_table(index='timestamp', columns='sell_buy', values='mean_price', aggfunc='sum', fill_value=0)
#     pivot['credit'] = pivot['sell'] - pivot['buy']
#     initial_credit = pivot['credit'].iloc[0]

#     # Calculate initial credit, stop loss, and take profit points
#     pivot['diff'] = pivot['credit'] - initial_credit
#     pivot['diff'] = pivot['diff'].round(2)  # Round the diff column to two decimal places

#     # Convert stop_loss and take_profit to float if they are strings
#     try:
#         stop_loss = float(stop_loss)
#         take_profit = float(take_profit)
#     except ValueError:
#         raise ValueError("Stop loss and take profit must be convertible to float.")

#     # Normalize unit input to uppercase
#     unit = unit.upper()

#     # Check for outcome based on DOLAR or PORCENTAJE unit
#     if unit == "DOLAR":
#         condition_loss = pivot['diff'] >= stop_loss
#         condition_profit = pivot['diff'] <= -take_profit
#     elif unit == "PORCENTAJE":
#         condition_loss = pivot['diff'] >= pivot['initial_credit'] * (stop_loss / 100)
#         condition_profit = pivot['diff'] <= -pivot['initial_credit'] * (take_profit / 100)
#     else:
#         raise ValueError("Invalid unit type specified. Must be 'DOLAR' or 'PORCENTAJE'.")

#     # Determine the first event of either reaching the stop loss or take profit
#     first_loss_time = pivot[condition_loss].index.min() if condition_loss.any() else None
#     first_profit_time = pivot[condition_profit].index.min() if condition_profit.any() else None

#     # Decide the outcome based on which condition is met first and include credit information
#     if first_loss_time and first_profit_time:
#         if first_profit_time < first_loss_time:
#             outcome = {
#                 'date': first_row_date,
#                 'Outcome': 'Good',
#                 'Time': first_profit_time,
#                 'Initial Credit': initial_credit,
#                 'Credit at Occurrence': pivot.at[first_profit_time, 'credit'],
#                 'Take Profit': take_profit,
#                 'Stop Loss': stop_loss,
#                 'sell_call_strike': sell_call_strike,
#                 'buy_call_strike': buy_call_strike,
#                 'sell_put_strike': sell_put_strike,
#                 'buy_put_strike': buy_put_strike
#             }
#         else:
#             outcome = {
#                 'date': first_row_date,
#                 'Outcome': 'Bad',
#                 'Time': first_loss_time,
#                 'Initial Credit': initial_credit,
#                 'Credit at Occurrence': pivot.at[first_loss_time, 'credit'],
#                 'Take Profit': take_profit,
#                 'Stop Loss': stop_loss,
#                 'sell_call_strike': sell_call_strike,
#                 'buy_call_strike': buy_call_strike,
#                 'sell_put_strike': sell_put_strike,
#                 'buy_put_strike': buy_put_strike
#             }
#     elif first_loss_time:
#         outcome = {
#             'date': first_row_date,
#             'Outcome': 'Bad',
#             'Time': first_loss_time,
#             'Initial Credit': initial_credit,
#             'Credit at Occurrence': pivot.at[first_loss_time, 'credit'],
#             'Take Profit': take_profit,
#             'Stop Loss': stop_loss,
#             'sell_call_strike': sell_call_strike,
#             'buy_call_strike': buy_call_strike,
#             'sell_put_strike': sell_put_strike,
#             'buy_put_strike': buy_put_strike
#         }
#     elif first_profit_time:
#         outcome = {
#             'date': first_row_date,
#             'Outcome': 'Good',
#             'Time': first_profit_time,
#             'Initial Credit': initial_credit,
#             'Credit at Occurrence': pivot.at[first_profit_time, 'credit'],
#             'Take Profit': take_profit,
#             'Stop Loss': stop_loss,
#             'sell_call_strike': sell_call_strike,
#             'buy_call_strike': buy_call_strike,
#             'sell_put_strike': sell_put_strike,
#             'buy_put_strike': buy_put_strike
#         }
#     else:
#         outcome = {
#             'date': first_row_date,
#             'Outcome': 'No outcome',
#             'Initial Credit': initial_credit,
#             'Credit at Occurrence': None,
#             'Take Profit': take_profit,
#             'Stop Loss': stop_loss,
#             'sell_call_strike': sell_call_strike,
#             'buy_call_strike': buy_call_strike,
#             'sell_put_strike': sell_put_strike,
#             'buy_put_strike': buy_put_strike
#         }

#     return outcome


# def get_all_credits(time_start, sell_call_strike, buy_call_strike, sell_put_strike, buy_put_strike, df):
#     # Ensure timestamp is in datetime format
#     if not pd.api.types.is_datetime64_any_dtype(df['timestamp']):
#         df['timestamp'] = pd.to_datetime(df['timestamp'])

#     # Filter only the relevant rows based on time and strike
#     df = df[df['timestamp'].dt.time >= pd.to_datetime(time_start).time()]
#     df = df[df['strike'].isin([sell_call_strike, buy_call_strike, sell_put_strike, buy_put_strike])]

#     # Calculate the mean prices for calls and puts directly into the DataFrame
#     df['mean_price'] = ((df['bid_call'] + df['ask_call']) / 2 * df['strike'].isin([sell_call_strike, buy_call_strike])
#                         + (df['bid_put'] + df['ask_put']) / 2 * df['strike'].isin([sell_put_strike, buy_put_strike])) * 100
#     # Create a flag to differentiate sell and buy
#     df['sell_buy'] = df['strike'].apply(lambda x: 'sell' if x in [sell_call_strike, sell_put_strike] else 'buy')
#     # Pivot the table to get sell and buy on separate columns, and compute the credit
#     pivot = df.pivot_table(index='timestamp', columns='sell_buy', values='mean_price', aggfunc='sum', fill_value=0)
#     pivot['credit'] = pivot['sell'] - pivot['buy']
#     # Drop the original 'sell' and 'buy' columns as they are no longer needed
#     pivot = pivot.drop(columns=['sell', 'buy'])
#     # Store the initial credit for reference
#     initial_credit = pivot['credit'].iloc[0]
#     # Calculate the difference in dollars and percentage and round them
#     pivot['dollar_diff'] = (initial_credit - pivot['credit']).round(2)
#     if initial_credit != 0:
#         pivot['percent_diff'] = ((pivot['dollar_diff'] / initial_credit) * 100).round(2)
#     else:
#         pivot['percent_diff'] = None
#     # Reset the index to flatten the DataFrame for easier reading
#     result = pivot.reset_index()
#     # Add strike values
#     result['sell_call_strike'] = sell_call_strike
#     result['buy_call_strike'] = buy_call_strike
#     result['sell_put_strike'] = sell_put_strike
#     result['buy_put_strike'] = buy_put_strike
#     # Save the result to CSV
#     result.to_csv('credit_results.csv', index=False)
#     return result
# Correr la aplicación en modo de desarrollo
if __name__ == "__main__":
    app.run(debug=True)
