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
import numpy as np
from concurrent.futures import ProcessPoolExecutor

    

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)

def load_csv(file_info):
    """Carga un CSV y devuelve su contenido."""
    file_date, file_path = file_info
    try:
        return file_date, pd.read_csv(file_path)
    except Exception as e:
        print(f"⚠ Error cargando {file_path}: {e}")
        return file_date, None  # Devolver None si hay error

def process_day_wrapper(args):
    """Función wrapper para `executor.map()`, desempaqueta `group` correctamente."""
    (file_date, day_df), horario, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit = args
    return process_day(day_df, horario, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit)


@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')  # Asegurar que se obtiene 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': []
        })

    # 🔹 Leer archivos en paralelo
    with ProcessPoolExecutor() as executor:
        grouped = list(executor.map(load_csv, files_with_dates))

    # Filtrar días donde la carga de datos falló
    grouped = [(date, df) for date, df in grouped if df is not None]

    # Crear una lista de argumentos para `executor.map()`
    args_list = [(group, horario, call_delta, put_delta, call_spread, put_spread, takeProfit, stopLoss, unit) for group in grouped]

    # 🔹 Procesar cada día en paralelo sin lambda
    with ProcessPoolExecutor() as executor:
        results = list(executor.map(process_day_wrapper, args_list))

    # Filtrar None (días descartados)
    results = [res for res in results if res 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


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 tipo 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

    # Extraer solo la hora de los timestamps para comparar
    df['only_time'] = df['Time'].dt.time

    # Filtrar solo los timestamps que son 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}")
        return None  # Retornar None si no hay datos

    # Calcular la diferencia de tiempo en segundos y encontrar el más cercano
    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].copy()

    # Obtener el timestamp exacto de apertura del Iron Condor
    opening_timestamp = closest_rows.iloc[0]['timestamp']

    # Mostrar en pantalla el timestamp más cercano encontrado y sus datos completos
    debug_print("Timestamp más cercano encontrado:")
    debug_print(closest_rows[['timestamp']].drop_duplicates())

    debug_print("\nFilas completas con el timestamp más cercano:")
    debug_print(closest_rows)

    # Calcular la diferencia de delta respecto a los valores deseados
    closest_rows['delta_call_diff'] = abs(closest_rows['delta_call'] - float(call_delta))
    closest_rows['delta_put_diff'] = abs(closest_rows['delta_put'] - float(put_delta))

    # Encontrar los strikes más cercanos a los deltas deseados (venta)
    idx_min_call_delta = closest_rows['delta_call_diff'].idxmin()
    idx_min_put_delta = closest_rows['delta_put_diff'].idxmin()

    sell_call = closest_rows.loc[idx_min_call_delta, 'strike']
    sell_put = closest_rows.loc[idx_min_put_delta, '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)

    # Mostrar los 4 strikes que conforman el 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(closest_rows, sell_call, buy_call, sell_put, buy_put)

    if credit is None:
        debug_print(f"🚨 Día descartado: {opening_timestamp[:10]} - No se pudo calcular el crédito.")
        return None  # Devuelve None y evita continuar con este día

    # Mostrar el crédito recibido
    debug_print(f"\n💰 Crédito Inicial del Iron Condor: {credit:.2f}")

    # **LLAMAMOS A LA NUEVA FUNCIÓN DESDE AQUÍ CON EL TIMESTAMP DE APERTURA**
    resultado, final_credit, occur_time = evaluate_iron_condor_performance(df, sell_call, buy_call, sell_put, buy_put, credit, takeProfit, stopLoss, opening_timestamp)

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

    return {
        "Time": opening_timestamp,
        "Outcome": resultado,
        "Initial Credit": credit,
        "Credit at Occurrence": final_credit,
        "Occur Time": occur_time,
        "Stop Loss": stopLoss,
        "Take Profit": takeProfit,
        "Sell Call Strike": sell_call,
        "Buy Call Strike": buy_call,
        "Sell Put Strike": sell_put,
        "Buy Put Strike": buy_put
    }


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 "Neutros", 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 "Neutros", 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)
