import os
import time
import pandas as pd
import numpy as np
from dotenv import load_dotenv
from datetime import datetime, timedelta
import re


#nohup /bin/python3 -u /var/www/html/xspChainOptions/allchains.py &
# Cargar variables de entorno
load_dotenv()

# Configuración global
PATH_UBUNTU = "/var/www/html/flask_project/"
INPUT_CSV = PATH_UBUNTU + "modelo/optionChain_$SPX_2025-02-10__.csv"
TIME_INTERVAL = '1min'  # Intervalo de 60 segundos

# Parámetros de suavización y umbrales
SMA_WINDOW = 5  # Ventana para suavizar tendencia
OI_GEX_DROP_THRESHOLD = 0.3  # Umbral de caída significativa en OI/GEX para considerar cambio
STRIKE_DYNAMIC_WINDOW = 10  # Minutos antes de actualizar strikes dominantes dinámicamente
STRIKE_RANGE = 50  # Reducimos el rango de selección de strikes cercanos al precio subyacente
HISTEREIS_WINDOW = 10  # Aumentamos el umbral de histéresis para reducir falsos cambios
PRICE_CHANGE_THRESHOLD = 5  # Umbral mínimo de cambio en el precio para considerar un cambio de tendencia


def load_data(file_path):
    """Carga los datos del archivo CSV en un DataFrame."""
    df = pd.read_csv(file_path, parse_dates=['timestamp'])
    df.sort_values(by=['timestamp'], inplace=True)
    df.set_index('timestamp', inplace=True)
    return df


def calculate_gex(data):
    total_gex = 0
    for row in data:
        gamma_call = row.get('gamma_call', 0)
        gamma_put = row.get('gamma_put', 0)
        open_interest_call = row.get('open_interest_call', 0)
        open_interest_put = row.get('open_interest_put', 0)

        row['gex_per_strike'] = (
            (gamma_call * open_interest_call) + (gamma_put * open_interest_put)
        ) * 100 if (gamma_call or gamma_put) else 0

        total_gex += row['gex_per_strike']

    return total_gex, data


def get_dominant_strikes(df, underlying_price):
    """Determina los strikes con mayor Open Interest, GEX y Volumen."""
    df_filtered = df[(df['strike'] >= underlying_price - STRIKE_RANGE) & (df['strike'] <= underlying_price + STRIKE_RANGE)].copy()
    df_filtered['total_open_interest'] = df_filtered['open_interest_call'] + df_filtered['open_interest_put']
    df_filtered['dominance_score'] = df_filtered['total_open_interest'] * df_filtered['gex_per_strike']
    df_filtered['total_volume'] = df_filtered['volume_call'] + df_filtered['volume_put']

    latest_timestamp = df_filtered.index.max()
    df_latest = df_filtered.loc[latest_timestamp]

    top_oi_strikes = df_latest[['strike', 'total_open_interest']].nlargest(2, 'total_open_interest')
    top_gex_strikes = df_latest[['strike', 'gex_per_strike']].nlargest(2, 'gex_per_strike')
    top_volume_strikes = df_latest[['strike', 'total_volume']].nlargest(2, 'total_volume')
    
    return {'oi': top_oi_strikes, 'gex': top_gex_strikes, 'volume': top_volume_strikes}


def prepare_features(df):
    df['gex_change'] = df.groupby('strike')['gex_per_strike'].diff()
    df['gex_change_smooth'] = df['gex_change'].rolling(window=SMA_WINDOW).mean()
    df['price_sma'] = df['underlying_price'].rolling(window=SMA_WINDOW).mean()
    df.dropna(inplace=True)
    return df


def calculate_strength(underlying_price, dominant_strikes):
    min_oi_strike = dominant_strikes['oi']['strike'].min()
    max_oi_strike = dominant_strikes['oi']['strike'].max()
    min_gex_strike = dominant_strikes['gex']['strike'].min()
    max_gex_strike = dominant_strikes['gex']['strike'].max()
    
    distance_to_nearest_oi = min(abs(underlying_price - min_oi_strike), abs(underlying_price - max_oi_strike))
    distance_to_nearest_gex = min(abs(underlying_price - min_gex_strike), abs(underlying_price - max_gex_strike))
    
    oi_distance_factor = 1 / (1 + distance_to_nearest_oi)
    gex_distance_factor = 1 / (1 + distance_to_nearest_gex)
    
    strength = (oi_distance_factor * 0.5) + (gex_distance_factor * 0.5)
    return round(min(max(strength, 0), 1), 2)


def predict_trend(df, last_trend='UP', hist_window=HISTEREIS_WINDOW):
    if df.empty:
        return pd.DataFrame(), last_trend

    df_resampled = df.resample(TIME_INTERVAL).agg({
        'underlying_price': 'last',
        'gex_total': 'sum',
        'open_interest_call': 'sum',
        'open_interest_put': 'sum',
        'gex_change': 'sum',
        'price_sma': 'last'
    })

    df_resampled.dropna(inplace=True)

    if df_resampled.empty:
        return pd.DataFrame(), last_trend

    underlying_price = df_resampled['underlying_price'].iloc[-1]
    previous_price = df_resampled['price_sma'].iloc[-1]
    dominant_strikes = get_dominant_strikes(df, underlying_price)

    strength = calculate_strength(underlying_price, dominant_strikes)
    
    min_oi_strike = dominant_strikes['oi']['strike'].min()
    max_oi_strike = dominant_strikes['oi']['strike'].max()
    min_gex_strike = dominant_strikes['gex']['strike'].min()
    max_gex_strike = dominant_strikes['gex']['strike'].max()
    
    if underlying_price > max_oi_strike and underlying_price > max_gex_strike and (underlying_price - previous_price) > PRICE_CHANGE_THRESHOLD:
        df_resampled['trend'] = 'UP'
        trend = 'UP'

    elif underlying_price < min_oi_strike and underlying_price < min_gex_strike and (previous_price - underlying_price) > PRICE_CHANGE_THRESHOLD:
        df_resampled['trend'] = 'DOWN'
        trend = 'DOWN'

    else:
        df_resampled['trend'] = last_trend
        trend = last_trend

    vertical_spread = suggest_vertical_spread(df, trend)
    
    df_resampled['reason'] = f"Tendencia {trend}: Strength = {strength} \n{vertical_spread} \nStrikes dominantes OI: {dominant_strikes['oi']['strike'].tolist()}, Strikes dominantes GEX: {dominant_strikes['gex']['strike'].tolist()}, Strikes dominantes Volumen: {dominant_strikes['volume']['strike'].tolist()}."
 
    # df_resampled['reason'] = f"Tendencia {df_resampled['trend'].iloc[-1]}: Strength = {strength}, Strikes dominantes OI: {dominant_strikes['oi']['strike'].tolist()}, Strikes dominantes GEX: {dominant_strikes['gex']['strike'].tolist()}, Strikes dominantes Volumen: {dominant_strikes['volume']['strike'].tolist()}."
    
    return df_resampled.reset_index(), df_resampled['trend'].iloc[-1], df_resampled['reason'].iloc[-1], strength, dominant_strikes['oi']['strike'].tolist(),dominant_strikes['gex']['strike'].tolist(), dominant_strikes['volume']['strike'].tolist(),vertical_spread


def suggest_vertical_spread(df, trend):
    latest_timestamp = df.index.max()
    df_latest = df.loc[latest_timestamp]
    
    if trend == 'UP':
        target_delta = -0.20
        closest_strike = df_latest.iloc[(df_latest['delta_put'] - target_delta).abs().argsort()[:1]]
        sell_strike = closest_strike['strike'].values[0]
        buy_strike = sell_strike - 5  # Spread de -5
        option_type = 'PUT'
    else:
        target_delta = 0.20
        closest_strike = df_latest.iloc[(df_latest['delta_call'] - target_delta).abs().argsort()[:1]]
        sell_strike = closest_strike['strike'].values[0]
        buy_strike = sell_strike + 5  # Spread de +5
        option_type = 'CALL'
    
    sell_price = df_latest[df_latest['strike'] == sell_strike][f'bid_{option_type.lower()}'].values[0]
    buy_price = df_latest[df_latest['strike'] == buy_strike][f'ask_{option_type.lower()}'].values[0]
    
    spread_price = round(sell_price - buy_price, 2)
    return f"{trend} Vertical: SELL -1 SPX {sell_strike}/{buy_strike} {option_type} @ {spread_price} LMT"

def predict_close_oi_gex(df):
    """Predice el cierre basado en el centro de gravedad de Open Interest y GEX."""
    df["oi_gex_weight"] = df["open_interest_call"] + df["open_interest_put"] + df["gex_per_strike"]
    predicted_close = (df["strike"] * df["oi_gex_weight"]).sum() / df["oi_gex_weight"].sum()
    return round(predicted_close, 2)


def predict_close_delta_neutral(df):
    """Predice el cierre basado en la teoría del Delta Neutro."""
    df["delta_total"] = df["delta_call"] + df["delta_put"]
    df["delta_abs"] = df["delta_total"].abs()
    predicted_close = (df["strike"] * df["delta_abs"]).sum() / df["delta_abs"].sum()
    return round(predicted_close, 2)


def predict_close_volume(df):
    """Predice el cierre basado en los strikes con mayor volumen."""
    df["total_volume"] = df["volume_call"] + df["volume_put"]
    predicted_close = (df["strike"] * df["total_volume"]).sum() / df["total_volume"].sum()
    return round(predicted_close, 2)


def calculate_market_gravity(df):
    """Calcula el punto de gravedad del mercado basado en fuerzas CALL y PUT."""
    df["F_put"] = df["open_interest_put"] * df["gamma_put"] * df["delta_put"] * df["volume_put"]
    df["F_call"] = df["open_interest_call"] * df["gamma_call"] * df["delta_call"] * df["volume_call"]
    
    df["F_total"] = df["F_call"] - df["F_put"]
    
    P_gravity = (df["strike"] * df["F_total"]).sum() / df["F_total"].abs().sum()
    
    return round(P_gravity, 2)


def predict_closing_prices(df):
    """Calcula las tres predicciones de cierre y el punto de gravedad del mercado."""
    return {
        "OI_GEX": predict_close_oi_gex(df),
        "Delta Neutro": predict_close_delta_neutral(df),
        "Volumen": predict_close_volume(df),
        "Punto de Gravedad": calculate_market_gravity(df)
    }

def save_to_csv(data, output_csv="predicciones_SPX.csv"):
    """Guarda los datos en un archivo CSV de manera acumulativa."""
    df = pd.DataFrame([data])
    
    # Verificar si el archivo ya existe
    if not os.path.exists(output_csv):
        df.to_csv(output_csv, index=False)
    else:
        df.to_csv(output_csv, mode='a', header=False, index=False)


def main():
    print("***** Iniciando monitoreo de tendencia del mercado *****")
    last_trend = 'UP'
    
    while True:
        try:
            print("Cargando datos...")
            df = load_data(INPUT_CSV)
            
            if len(df) < SMA_WINDOW:
                print("No hay suficientes datos en el archivo para calcular la tendencia.")
            else:
                df_prepared = prepare_features(df)
                df_trend, last_trend, reason, strength, OI_dominantes, GEX_dominantes,Volume_dominantes,idea = predict_trend(df_prepared, last_trend)
                predicted_closes = predict_closing_prices(df)
                
                if not df_trend.empty:
                    latest_prediction = df_trend.iloc[-1]
                    timestamp = latest_prediction['timestamp'].strftime('%Y-%m-%d %H:%M:%S')
                    print(f"{timestamp} - SPX precio actual ${latest_prediction['underlying_price']:.2f} {latest_prediction['trend']} tendencia")
                    print(f"Razón: {reason} v5.4")
                    print(f"Predicciones de cierre:")
                    print(f" - OI_GEX: {predicted_closes['OI_GEX']}")
                    print(f" - Delta Neutro: {predicted_closes['Delta Neutro']}")
                    print(f" - Volumen: {predicted_closes['Volumen']}")
                    print(f" - Punto de Gravedad del Mercado: {predicted_closes['Punto de Gravedad']}")

                    data_to_save = {
                    "timestamp": timestamp,
                    "precio_actual": latest_prediction['underlying_price'],
                    "tendencia": latest_prediction['trend'],
                    "strength": strength,
                    "strikes_OI": OI_dominantes,
                    "strikes_GEX": GEX_dominantes,
                    "strikes_Volumen": Volume_dominantes,
                    "prediccion_OI_GEX": predicted_closes['OI_GEX'],
                    "prediccion_Delta_Neutro": predicted_closes['Delta Neutro'],
                    "prediccion_Volumen": predicted_closes['Volumen'],
                    "punto_de_gravedad": predicted_closes['Punto de Gravedad'],
                    "idea": idea
                }
                save_to_csv(data_to_save)
        except Exception as e:
            print(f"Error en la predicción: {e}")
        time.sleep(60)

if __name__ == "__main__":
    main()