from modules import api, stream
from modules import terminal
import time
from datetime import datetime, timedelta
import csv
import datetime
import os
import pytz
import json
import requests
import sys
import pprint
import traceback


# Cambiar al directorio
os.chdir('/home/seba/projects/VPS_Streaming_CS')
PATH_UBUNTU = "/var/www/html/flask_project/"
# PATH_UBUNTU = "/home/seba/projects/VPS_Streaming_CS/"


MAX_RETRIES = 3
RETRY_DELAY_SECONDS = 30

SYMBOL = "$SPX"
# from_date = "2024-06-03"
# to_date = "2024-06-03"

from_date = datetime.datetime.now().strftime("%Y-%m-%d")
to_date = datetime.datetime.now().strftime("%Y-%m-%d")
totalStrikes = 45

timeOpen = "09:29:00"
timeClose = "16:00:00"

timeIB1 = "09:40:00"
timeIB1Close = "16:00:00"

timeIB2 = "10:30:00"
timeIB2Close = "16:00:00"

timeIB3 = "11:00:00"
timeIB3Close = "16:00:00"

timeIB4 = "11:30:00"
timeIB4Close= "16:00:00"

timeIB5 = "09:31:00"
timeIB5Close = "16:00:00"

timeIB6 = "09:31:00"
timeIB6Close = "16:00:00"

timeIB7 = "09:31:00"
timeIB7Close = "16:00:00"

timeIB8 = "10:30:00"
timeIB8Close = "16:00:00"


# timeOpen = "00:00:00"
# timeClose = "23:59:00"

# timeIB1 = "00:05:00"
# timeIB1Close = "23:59:00"

# timeIB2 = "00:05:00"
# timeIB2Close = "23:59:00"

# timeIB3 = "00:05:00"
# timeIB3Close = "23:59:00"

# timeIB4 = "00:05:00"
# timeIB4Close = "23:59:00"

# timeIB5 = "00:05:00"
# timeIB5Close = "23:59:00"

# timeIB6 = "00:05:00"
# timeIB6Close = "23:59:00"

# timeIB7 = "00:05:00"
# timeIB7Close = "23:59:00"

# timeIB8 = "00:05:00"
# timeIB8Close = "23:59:00"

fechaActual = datetime.datetime.now().strftime("%Y-%m-%d")
archivo_csv1 = PATH_UBUNTU + "Intraday_IB0940_" + str(fechaActual) + ".csv"
archivo_csv2 = PATH_UBUNTU + "Intraday_IB1030_" + str(fechaActual) + ".csv"
archivo_csv3 = PATH_UBUNTU + "Intraday_IB1100_" + str(fechaActual) + ".csv"
archivo_csv4 = PATH_UBUNTU + "Intraday_IB1130_" + str(fechaActual) + ".csv"
archivo_csv5 = PATH_UBUNTU + "Intraday_IB0932_" + str(fechaActual) + ".csv"  # IB 9:31 30 PUNTOS
archivo_csv6 = PATH_UBUNTU + "Intraday_IB0933_" + str(fechaActual) + ".csv"  # IB 9:31 50 PUNTOS
archivo_csv7 = PATH_UBUNTU + "Intraday_IC0932_" + str(fechaActual) + ".csv"  # IC 9:31 DELTA .10 - 20 ANCHO DE ALAS
archivo_csv8 = PATH_UBUNTU + "Intraday_IC1030_" + str(fechaActual) + ".csv"  # IC 10:30 DELTA .10 - 20 ANCHO DE ALAS


# Funcion para obtener quotes con reintento
def obtener_quotes_con_reintento(instruments):
    for intento in range(MAX_RETRIES):
        try:
            quotes_response = api.quotes.getList(instruments).json()
            return quotes_response
        except json.decoder.JSONDecodeError as json_error:
            print(
                f"Error al decodificar JSON en el intento {intento + 1}: {json_error}")
        except requests.RequestException as request_error:
            print(
                f"Error en la solicitud HTTP en el intento {intento + 1}: {request_error}")
        except Exception as e:
            print(
                f"Otro error inesperado en el intento {intento + 1}: {type(e).__name__}: {e}")
        time.sleep(RETRY_DELAY_SECONDS)
    else:
        terminal.colorPrint.error(f"Se ha producido un error en {MAX_RETRIES} intentos. Deteniendo el programa.")
        sys.exit(1)


# Funcion para obtener cadena de opciones con reintento
def obtener_cadena_con_reintento(SYMBOL, from_date, to_date, totalStrikes):

    for intento in range(MAX_RETRIES):
        try:
            chain_response = api.options.chains(SYMBOL, fromDate=from_date, toDate=to_date, strikeCount=totalStrikes)
            return chain_response
        except json.decoder.JSONDecodeError as json_error:
            print(f"Error al decodificar JSON en el intento {intento + 1}: {json_error}")
        except requests.RequestException as request_error:
            print(f"Error en la solicitud HTTP en el intento {intento + 1}: {request_error}")
        except Exception as e:
            print(f"Otro error inesperado en el intento {intento + 1}: {type(e).__name__}: {e}")
        time.sleep(RETRY_DELAY_SECONDS)
    else:
        terminal.colorPrint.error(f"Se ha producido un error en {MAX_RETRIES} intentos. Deteniendo el programa.")
        sys.exit(1)


# Función para extraer datos de opciones
def extract_data(option_data):
    extracted_data = {}
    timestamp = datetime.datetime.now(pytz.timezone('America/New_York')).strftime('%m/%d/%Y %H:%M:%S')
    underlying_price = option_data.get('underlyingPrice', '')
    for option_type in ['callExpDateMap', 'putExpDateMap']:
        if option_type in option_data:
            for date_key, strikes in option_data[option_type].items():
                for strike, details in strikes.items():
                    for detail in details:
                        strike_price = detail.get('strikePrice', '')
                        bid = detail.get('bid', '')
                        ask = detail.get('ask', '')
                        delta = detail.get('delta', '')
                        if strike_price not in extracted_data:
                            extracted_data[strike_price] = {'timestamp': timestamp, 'underlying_price': underlying_price, 'strike': strike_price}
                        if option_type == 'callExpDateMap':
                            extracted_data[strike_price]['bid_call'] = bid
                            extracted_data[strike_price]['ask_call'] = ask
                            extracted_data[strike_price]['delta_call'] = delta
                        else:
                            extracted_data[strike_price]['bid_put'] = bid
                            extracted_data[strike_price]['ask_put'] = ask
                            extracted_data[strike_price]['delta_put'] = delta
    return list(extracted_data.values())


# Función para guardar la cadena de opciones en un archivo CSV
def save_chain_to_cvs(data):
    fechaActual = datetime.datetime.now().strftime("%Y-%m-%d")
    filename = PATH_UBUNTU + "optionChain_" + str(fechaActual) + ".csv"
    header = ['timestamp', 'underlying_price', 'strike', 'bid_call', 'ask_call', 'bid_put', 'ask_put', 'delta_call', 'delta_put']
    file_exists = os.path.isfile(filename)
    with open(filename, mode='a', newline='') as file:
        writer = csv.DictWriter(file, fieldnames=header)
        if not file_exists:
            writer.writeheader()
            os.chmod(filename, 0o664)  # El modo octal 0o664 es equivalente a rw-rw-r--

        writer.writerows(data)


# Función para calcular el precio del Iron Condor
def calculate_iron_condor_price(data_to_save, call_strike_sell, call_strike_buy, put_strike_sell, put_strike_buy):
    global totalStrikes
    global from_date 
    global to_date 

    attempt = 0
    
    while attempt < MAX_RETRIES:
        try:
            # Incrementar el contador de intentos
            attempt += 1

            # Filtrar los datos relevantes
            relevant_data = [row for row in data_to_save if str(row['strike']) in [call_strike_sell, call_strike_buy, put_strike_sell, put_strike_buy]]
            if not relevant_data:
                raise ValueError("No relevant data found for the given strike prices.")

            call_strike_sell_data = next((item for item in relevant_data if str(item['strike']) == call_strike_sell), None)
            call_strike_buy_data  = next((item for item in relevant_data if str(item['strike']) == call_strike_buy), None)
            put_strike_sell_data  = next((item for item in relevant_data if str(item['strike']) == put_strike_sell), None)
            put_strike_buy_data   = next((item for item in relevant_data if str(item['strike']) == put_strike_buy), None)

            if call_strike_sell_data is None:
                raise ValueError(f"No data found for call strike sell price: {call_strike_sell}")
            if call_strike_buy_data is None:
                raise ValueError(f"No data found for call strike buy price: {call_strike_buy}")
            if put_strike_sell_data is None:
                raise ValueError(f"No data found for put strike sell price: {put_strike_sell}")
            if put_strike_buy_data is None:
                raise ValueError(f"No data found for put strike buy price: {put_strike_buy}")

            # Si todos los datos específicos se encuentran, salir del bucle
            break

        except Exception as e:
            terminal.colorPrint.error(f"Error en el intento {attempt}: {e}")

            # Incrementar totalStrikes en 5 para el siguiente intento
            totalStrikes += 5

            # Volver a obtener la cadena de opciones y extraer los datos
            try:
                options_chain = obtener_cadena_con_reintento(SYMBOL, from_date, to_date, totalStrikes).json()
                data_to_save = extract_data(options_chain)
            except Exception as e:
                terminal.colorPrint.error(f"Error al regenerar data_to_save: {e}")
                raise

            # Si el número de intentos excede el máximo permitido, lanzar una excepción
            if attempt >= MAX_RETRIES:
                terminal.colorPrint.error("Max retries exceeded. Could not find all relevant data.")
                # Volver a imprimir el error y la traza fuera del bucle
                raise

    # Calcular los precios del Iron Condor
    sellCall = (call_strike_sell_data['ask_call'] + call_strike_sell_data['bid_call']) / 2
    buyCall = (call_strike_buy_data['ask_call'] + call_strike_buy_data['bid_call']) / 2
    sellPut = (put_strike_sell_data['ask_put'] + put_strike_sell_data['bid_put']) / 2
    buyPut = (put_strike_buy_data['ask_put'] + put_strike_buy_data['bid_put']) / 2

    # Calcular el precio del Iron Condor
    iron_condor_price = (sellCall - buyCall + sellPut - buyPut) * 100
    iron_condor_price = "{:.2f}".format(iron_condor_price)

    return iron_condor_price


def save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, elVIX, filename):
    attempts = 0
    while attempts < MAX_RETRIES:
        try:
            with open(filename, mode='a', newline='') as file:
                writer = csv.writer(file)
                writer.writerow([timestamp, iron_condor_price, underlying_price, elVIX])
            break  # Salir del bucle si se guarda exitosamente
        except Exception as e:
            terminal.colorPrint.error(f"Ocurrió un error al guardar en el archivo CSV: {e}")
            attempts += 1
            if attempts < MAX_RETRIES:
                terminal.colorPrint.info("Intentando de nuevo en 5 segundos...")
                time.sleep(5)
            else:
                terminal.colorPrint.error("No se pudo guardar en el archivo CSV después de 3 intentos. Deteniendo el programa.")
                raise  # Levantar la excepción para que el programa termine



def obtenerFiguraIC(archivo, sellCall, sellPut, buyCall, buyPut):
    # Chequear si existe el archivo
    if os.path.isfile(archivo):
        terminal.colorPrint.info("The CSV file exists. Getting Data From File.")
        with open(archivo, 'r') as archivo_csv:
            lector_csv = csv.reader(archivo_csv)
            primera_fila = next(lector_csv)
            sellCall = primera_fila[0]
            sellPut = primera_fila[1]
            buyCall = primera_fila[2]
            buyPut = primera_fila[3]

        terminal.colorPrint.info(f'[BUY CALL:{buyCall}] [SELL CALL:{sellCall}] [SELL PUT:{sellPut}] [BUY PUT:{buyPut}]')
    else:
        terminal.colorPrint.info("The CSV file don't exists. Getting Data From Real Time.")
        spxStrike = 0
        elSPX = obtener_quotes_con_reintento(instruments=["$SPX"])
        if (elSPX is not None and "$SPX" in elSPX and "quote" in elSPX["$SPX"] and "lastPrice" in elSPX["$SPX"]["quote"]):
            SPXLastPrice = elSPX["$SPX"]["quote"]["lastPrice"]
            mod = SPXLastPrice % 5
            if mod > 2.5:
                spxStrike = int(SPXLastPrice - mod) + 5
            else:
                spxStrike = int(SPXLastPrice - mod)

            sellCall = spxStrike+sellCall
            sellPut = spxStrike+sellPut
            buyCall = spxStrike+buyCall
            buyPut = spxStrike-buyPut

            terminal.colorPrint.info(f'[BUY CALL:{buyCall}] [SELL CALL:{sellCall}] [SELL PUT:{sellPut}] [BUY PUT:{buyPut}]')

            with open(archivo, 'w', newline='') as archivo_csv:
                escritor_csv = csv.writer(archivo_csv)
                escritor_csv.writerow([sellCall, sellPut, buyCall, buyPut])

            # Dar permisos de lectura y escritura para todos los usuarios
            os.chmod(archivo, 0o664)

    return sellCall, sellPut, buyCall, buyPut


# Función para extraer los valores de delta y strike
def extract_delta_strike_pairs(option_data):
    delta_strike_pairs = []
    # Recorrer las claves del mapa de expiración de llamadas
    for exp_date, strikes in option_data.get('callExpDateMap', {}).items():
        for strike, options in strikes.items():
            for option in options:
                if 'delta' in option:
                    delta_strike_pairs.append((option['delta'], float(strike)))

    # Recorrer las claves del mapa de expiración de ventas
    for exp_date, strikes in option_data.get('putExpDateMap', {}).items():
        for strike, options in strikes.items():
            for option in options:
                if 'delta' in option:
                    delta_strike_pairs.append((option['delta'], float(strike)))

    return delta_strike_pairs

# Función para encontrar el strike más cercano a un delta objetivo


def find_closest_strike(delta_strike_pairs, target_delta):
    closest_pair = min(delta_strike_pairs, key=lambda x: abs(x[0] - target_delta))
    return closest_pair


def obtenerFiguraICDelta(archivo, deltaCall, deltaPut, anchoCall, anchoPut):
    # Chequear si existe el archivo
    if os.path.isfile(archivo):
        terminal.colorPrint.info("The CSV file exists. Getting Data From File.")
        with open(archivo, 'r') as archivo_csv:
            lector_csv = csv.reader(archivo_csv)
            primera_fila = next(lector_csv)
            sellCall = primera_fila[0]
            sellPut = primera_fila[1]
            buyCall = primera_fila[2]
            buyPut = primera_fila[3]

        terminal.colorPrint.info(f'[BUY CALL:{buyCall}] [SELL CALL:{sellCall}] [SELL PUT:{sellPut}] [BUY PUT:{buyPut}]')
    else:
        terminal.colorPrint.info("The CSV file don't exists. Getting Data From Real Time.")
        options_chain = (api.options.chains(SYMBOL, fromDate=from_date, toDate=to_date, strikeCount=totalStrikes).json())

        # Llamar a la función y obtener los pares de delta y strike
        delta_strike_pairs = extract_delta_strike_pairs(options_chain)
        # Encontrar los strikes más cercanos
        strikeDeltaCall = find_closest_strike(delta_strike_pairs, deltaCall)
        strikeDeltaPut = find_closest_strike(delta_strike_pairs, deltaPut)

        # # Mostrar los resultados
        terminal.colorPrint.info(f"El strike mas cercano al delta 0.10 es : {strikeDeltaCall[1]} con delta {strikeDeltaCall[0]}")
        terminal.colorPrint.info(f"El strike mas cercano al delta -0.10 es: {strikeDeltaPut[1]} con delta {strikeDeltaPut[0]}")

        sellCall = int(float(strikeDeltaCall[1]))
        sellPut = int(float(strikeDeltaPut[1]))
        buyCall = sellCall + anchoCall
        buyPut = sellPut - anchoPut

        terminal.colorPrint.info(f'[BUY CALL:{buyCall}] [SELL CALL:{sellCall}] [SELL PUT:{sellPut}] [BUY PUT:{buyPut}]')

        with open(archivo, 'w', newline='') as archivo_csv:
            escritor_csv = csv.writer(archivo_csv)
            escritor_csv.writerow([sellCall, sellPut, buyCall, buyPut])

        # Dar permisos de lectura y escritura para todos los usuarios
        os.chmod(archivo, 0o664)

    return sellCall, sellPut, buyCall, buyPut


def main():
    global totalStrikes
    terminal.colorPrint.info("***** Market opened!!! *****")

    IB1_activado = False
    IB2_activado = False
    IB3_activado = False
    IB4_activado = False
    IB5_activado = False
    IB6_activado = False
    IB7_activado = False
    IB8_activado = False

    while True:
        hora_actual = time.strftime("%H:%M:%S")
        options_chain = obtener_cadena_con_reintento(SYMBOL, from_date, to_date, totalStrikes).json()
        data_to_save = extract_data(options_chain)
        underlying_price = data_to_save[0]['underlying_price']
        timestamp = data_to_save[0]['timestamp']

        # Obteniendo el VIX
        elVIX = obtener_quotes_con_reintento(instruments=["$VIX"])
        if (elVIX is not None and "$VIX" in elVIX and "quote" in elVIX["$VIX"] and "lastPrice" in elVIX["$VIX"]["quote"]):
            VIXLastPrice = elVIX["$VIX"]["quote"]["lastPrice"]

        # terminal.colorPrint.quote(f"Time: {hora_actual} - SPX: {SPXLastPrice} - VIX: {VIXLastPrice}")
        terminal.colorPrint.spx(str(underlying_price))
        terminal.colorPrint.vix(str(VIXLastPrice))
        # Comenzar el streaming para IB 9:30 con alas de 30
        if hora_actual >= timeIB5 and hora_actual <= timeIB5Close:
            if IB5_activado == False:
                terminal.colorPrint.info("Starting IB 9:30... Wings 30")

                # Chequear si archivo existe
                sellCall, sellPut, buyCall, buyPut = obtenerFiguraIC(archivo_csv5, 0, 0, 30, 30)

                call_strike_sell5 = str(sellCall) + ".0"
                call_strike_buy5 = str(buyCall) + ".0"
                put_strike_sell5 = str(sellPut) + ".0"
                put_strike_buy5 = str(buyPut) + ".0"

                IB5_activado = True

            iron_condor_price = calculate_iron_condor_price(data_to_save, call_strike_sell5, call_strike_buy5, put_strike_sell5, put_strike_buy5)
            save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, VIXLastPrice, archivo_csv5)
            terminal.colorPrint.quote(f"{timestamp} {iron_condor_price} [IB 9:30 0DTE W30]")

        # Comenzar el streaming para IB 9:30 con alas de 50
        if hora_actual >= timeIB6 and hora_actual <= timeIB6Close:
            if IB6_activado == False:
                terminal.colorPrint.info("Starting IB 9:30... Wings 50")

                # Chequear si archivo existe
                sellCall, sellPut, buyCall, buyPut = obtenerFiguraIC(archivo_csv6, 0, 0, 50, 50)
                call_strike_sell6 = str(sellCall) + ".0"
                call_strike_buy6 = str(buyCall) + ".0"
                put_strike_sell6 = str(sellPut) + ".0"
                put_strike_buy6 = str(buyPut) + ".0"

                IB6_activado = True

            iron_condor_price = calculate_iron_condor_price(data_to_save, call_strike_sell6, call_strike_buy6, put_strike_sell6, put_strike_buy6)
            save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, VIXLastPrice, archivo_csv6)
            terminal.colorPrint.quote(f"{timestamp} {iron_condor_price} [IB 9:30 0DTE W50]")

        # Comenzar el streaming para IC 9:30 con delta 10 y ancho de 20
        if hora_actual >= timeIB7 and hora_actual <= timeIB7Close:
            if IB7_activado == False:
                terminal.colorPrint.info("Starting IC 9:30... Delta 10 - Ancho alas de 20")

                # Chequear si archivo existe
                sellCall, sellPut, buyCall, buyPut = obtenerFiguraICDelta(archivo_csv7, 0.10, -0.10, 20, 20)

                call_strike_sell7 = str(sellCall) + ".0"
                call_strike_buy7 = str(buyCall) + ".0"
                put_strike_sell7 = str(sellPut) + ".0"
                put_strike_buy7 = str(buyPut) + ".0"

                IB7_activado = True

            iron_condor_price = calculate_iron_condor_price(data_to_save, call_strike_sell7, call_strike_buy7, put_strike_sell7, put_strike_buy7)
            save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, VIXLastPrice, archivo_csv7)
            terminal.colorPrint.quote(f"{timestamp} {iron_condor_price} [IC  9:30 0DTE D10 W20]")

        # Comenzar el streaming para el 1er IB 09:40
        if hora_actual >= timeIB1 and hora_actual <= timeIB1Close:
            if IB1_activado == False:
                terminal.colorPrint.info("Starting IB 09:40... Wings 40")
                # Chequear si archivo existe
                sellCall, sellPut, buyCall, buyPut = obtenerFiguraIC(archivo_csv1, 0, 0, 40, 40)

                call_strike_sell1 = str(sellCall) + ".0"
                call_strike_buy1 = str(buyCall) + ".0"
                put_strike_sell1 = str(sellPut) + ".0"
                put_strike_buy1 = str(buyPut) + ".0"
                IB1_activado = True

            iron_condor_price = calculate_iron_condor_price(data_to_save, call_strike_sell1, call_strike_buy1, put_strike_sell1, put_strike_buy1)
            save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, VIXLastPrice, archivo_csv1)
            terminal.colorPrint.quote(f"{timestamp} {iron_condor_price} [IB 9:40 0DTE W40]")

        # Comenzar el streaming para IC 10:30 con delta 10 y ancho de 20
        if hora_actual >= timeIB8 and hora_actual <= timeIB8Close:
            if IB8_activado == False:
                terminal.colorPrint.info("Starting IC 10:30... Delta 10 - Ancho alas de 20")

                # Chequear si archivo existe
                sellCall, sellPut, buyCall, buyPut = obtenerFiguraICDelta(archivo_csv8, 0.10, -0.10, 20, 20)

                call_strike_sell8 = str(sellCall) + ".0"
                call_strike_buy8 = str(buyCall) + ".0"
                put_strike_sell8 = str(sellPut) + ".0"
                put_strike_buy8 = str(buyPut) + ".0"

                IB8_activado = True

            iron_condor_price = calculate_iron_condor_price(data_to_save, call_strike_sell8, call_strike_buy8, put_strike_sell8, put_strike_buy8)
            save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, VIXLastPrice, archivo_csv8)
            terminal.colorPrint.quote(f"{timestamp} {iron_condor_price} [IC 10:30 0DTE D10 W20]")

        # Comenzar el streaming para el IB 10:30
        if hora_actual >= timeIB2 and hora_actual <= timeIB2Close:
            if IB2_activado == False:
                terminal.colorPrint.info("Starting IB 10:30... Wings 40")

                # Chequear si archivo existe
                sellCall, sellPut, buyCall, buyPut = obtenerFiguraIC(archivo_csv2, 0, 0, 40, 40)

                call_strike_sell2 = str(sellCall) + ".0"
                call_strike_buy2 = str(buyCall) + ".0"
                put_strike_sell2 = str(sellPut) + ".0"
                put_strike_buy2 = str(buyPut) + ".0"

                IB2_activado = True

            iron_condor_price = calculate_iron_condor_price(data_to_save, call_strike_sell2, call_strike_buy2, put_strike_sell2, put_strike_buy2)
            save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, VIXLastPrice, archivo_csv2)
            terminal.colorPrint.quote(f"{timestamp} {iron_condor_price} [IB 10:30 0DTE W40]")

        # Comenzar el streaming para IB 11:00
        if hora_actual >= timeIB3 and hora_actual <= timeIB3Close:
            if IB3_activado == False:
                terminal.colorPrint.info("Starting IB 11:00... Wings 40")

                # Chequear si archivo existe
                sellCall, sellPut, buyCall, buyPut = obtenerFiguraIC(
                    archivo_csv3, 0, 0, 40, 40)

                call_strike_sell3 = str(sellCall) + ".0"
                call_strike_buy3 = str(buyCall) + ".0"
                put_strike_sell3 = str(sellPut) + ".0"
                put_strike_buy3 = str(buyPut) + ".0"

                IB3_activado = True

            iron_condor_price = calculate_iron_condor_price(data_to_save, call_strike_sell3, call_strike_buy3, put_strike_sell3, put_strike_buy3)
            save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, VIXLastPrice, archivo_csv3)
            terminal.colorPrint.quote(f"{timestamp} {iron_condor_price} [IB 11:00 0DTE W40]")

        # Comenzar el streaming para el IB 11:30
        if hora_actual >= timeIB4 and hora_actual <= timeIB4Close:
            if IB4_activado == False:
                terminal.colorPrint.info("Starting IB 11:30... Wings 40")
                # Chequear si archivo existe
                sellCall, sellPut, buyCall, buyPut = obtenerFiguraIC(archivo_csv4, 0, 0, 40, 40)

                call_strike_sell4 = str(sellCall) + ".0"
                call_strike_buy4 = str(buyCall) + ".0"
                put_strike_sell4 = str(sellPut) + ".0"
                put_strike_buy4 = str(buyPut) + ".0"

                IB4_activado = True

            iron_condor_price = calculate_iron_condor_price(data_to_save, call_strike_sell4, call_strike_buy4, put_strike_sell4, put_strike_buy4)
            save_iron_condor_to_csv(timestamp, iron_condor_price, underlying_price, VIXLastPrice, archivo_csv4)
            terminal.colorPrint.quote(f"{timestamp} {iron_condor_price} [IB 11:30 0DTE W40]")

        # Guarda la cadena de opciones
        save_chain_to_cvs(data_to_save)

        if hora_actual>=timeClose:
            return
        time.sleep(9)


def timeToMarketOpen(hora_actual, hora_apertura):
    diferencia_tiempo = hora_apertura - hora_actual
    # Imprimir la diferencia de tiempo en formato hh:mm:ss
    print(f"V6 - Time to begin process: {diferencia_tiempo // timedelta(hours=1):02}:{(diferencia_tiempo % timedelta(hours=1)) // timedelta(minutes=1):02}:{(diferencia_tiempo % timedelta(minutes=1)) // timedelta(seconds=1):02}", end="\r", flush=True)
    time.sleep(1)


def is_holiday(fecha, holidays):
    # Verificar si la fecha está en la lista de días feriados
    return fecha.strftime("%m/%d/%Y") in holidays


def calcular_hora_apertura(hora_actual, hora_primera_estrategia, holidays):
    dias_a_sumar = 1

    while True:
        # Calcular la nueva fecha sumando los días correspondientes
        nueva_fecha = hora_actual.date() + timedelta(days=dias_a_sumar)

        # Verificar si la nueva fecha es un día feriado o fin de semana (sábado o domingo)

        if not (is_holiday(nueva_fecha, holidays) or nueva_fecha.weekday() in [5, 6]):
            break  # Salir del bucle si no es un día feriado ni fin de semana

        # Si es un día feriado o fin de semana, sumar un día adicional y verificar nuevamente
        dias_a_sumar += 1

    # Calcular la nueva hora_apertura
    nueva_hora_apertura = datetime.datetime.combine(
        nueva_fecha, hora_primera_estrategia)
    print(nueva_hora_apertura)

    return nueva_hora_apertura


# Comienzo del progragma!!!
os.system("clear")  # Borra la pantalla!!!

marketIsOpen = False
hora_actual_string = time.strftime("%H:%M:%S")
hora_actual = datetime.datetime.now()
hora_cierre = datetime.datetime.strptime(timeClose, "%H:%M:%S").time()
hora_market_open = datetime.datetime.strptime(timeOpen, "%H:%M:%S").time()
holidays = ["01/01/2024", "01/15/2024", "02/19/2024", "05/27/2024", "06/19/2024", "07/04/2024", "09/02/2024", "11/28/2024", "12/25/2024"]

# Lógica para determinar hora_apertura
if hora_actual.time() > hora_cierre and hora_actual.time() < datetime.time(23, 59, 59):
    # Si es de tarde, obtener la fecha del día siguiente
    hora_apertura = datetime.datetime.combine(hora_actual.date() + timedelta(days=1), hora_market_open)
else:
    # Si es mañana, obtener la fecha actual
    hora_apertura = datetime.datetime.combine(hora_actual.date(), hora_market_open)

try:
    while True:
        hora_actual = datetime.datetime.now()
        timeToMarketOpen(hora_actual, hora_apertura)

        if hora_actual >= hora_apertura:
            os.system("clear")  # Borra la pantalla!!!
            marketIsOpen = True  # Comenzar el streaming para el 1er estragegia!!!

        if marketIsOpen:
            api.initialize()  # checks tokens & loads variables
            api.updateTokensAutomatic()  # starts thread to update tokens automatically
            main()
            # Si salio de main, es porque el mercado cerró y volvera a iniciarse el proceso de espera!!!!

            print("\n***** The market is closed *****\n")
            marketIsOpen = False
            hora_apertura = calcular_hora_apertura(hora_actual, hora_market_open, holidays)


except Exception as e:
    error_message = f"Error occurred: {str(e)}\n\n{traceback.format_exc()}"
    # Envía un correo electrónico o notificación
