import pandas as pd
import matplotlib.pyplot as plt
import math

# ========================= CONFIGURACIÓN =========================

# Colores consola
RED = "\033[91m"
GREEN = "\033[92m"
RESET = "\033[0m"

def colorize(value):
    if isinstance(value, (int, float)):
        color = GREEN if value >= 0 else RED
        return f"{color}{value}{RESET}"
    return value

# Paths
PATH_UBUNTU = "/var/www/html/flask_project/"

# Parámetros
desde = "2025-10-01"
hasta = "2025-12-24"
symbol = "SPX"
timeHour = "1245"
desplazamiento = -15
estrategia = "Vertical"

# TP / SL
USE_TP_SL = True
TP = 150    # dólares
SL = 250   # dólares

elArchivo = f"/var/www/html/backtestingmarket/predictor_data/makekos/{symbol}/{symbol}_{estrategia}_strikes_{timeHour}.csv"

# ========================= FUNCIONES =========================

def calcular_credito_y_underlying_desde_archivo_csv(
    archivo_option_chain, strike1, strike2, option_type, timeHour
):
    df = pd.read_csv(archivo_option_chain, usecols=range(9))
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    df = df[df['timestamp'] >= timeHour]

    if df.empty:
        return None, None

    s1 = df[df['strike'] == strike1].iloc[0]
    s2 = df[df['strike'] == strike2].iloc[0]

    if option_type == "CALL":
        p1 = (s1['bid_call'] + s1['ask_call']) / 2
        p2 = (s2['bid_call'] + s2['ask_call']) / 2
    else:
        p1 = (s1['bid_put'] + s1['ask_put']) / 2
        p2 = (s2['bid_put'] + s2['ask_put']) / 2

    return p1 - p2, s1['underlying_price']


def obtener_ultimo_underlying_price(archivo_option_chain):
    try:
        df = pd.read_csv(archivo_option_chain)
        df['timestamp'] = pd.to_datetime(df['timestamp'])
        return df.sort_values('timestamp')['underlying_price'].iloc[-1]
    except:
        return None


def simular_tp_sl(
    archivo_option_chain,
    strike1,
    strike2,
    option_type,
    entry_time,
    credit,
    TP,
    SL
):
    try:
        df = pd.read_csv(archivo_option_chain)
        df['timestamp'] = pd.to_datetime(df['timestamp'])
        df = df[df['timestamp'] >= entry_time]
    except:
        return None, None, None

    for ts, snap in df.groupby('timestamp'):
        s1 = snap[snap['strike'] == strike1]
        s2 = snap[snap['strike'] == strike2]
        if s1.empty or s2.empty:
            continue

        if option_type == "CALL":
            p1 = (s1['bid_call'].iloc[0] + s1['ask_call'].iloc[0]) / 2
            p2 = (s2['bid_call'].iloc[0] + s2['ask_call'].iloc[0]) / 2
        else:
            p1 = (s1['bid_put'].iloc[0] + s1['ask_put'].iloc[0]) / 2
            p2 = (s2['bid_put'].iloc[0] + s2['ask_put'].iloc[0]) / 2

        spread_price = p1 - p2
        pnl = (credit - spread_price) * 100

        if pnl >= TP:
            return TP, "TP", ts
        if pnl <= -SL:
            return -SL, "SL", ts

    return None, None, None


# ========================= BACKTEST PRINCIPAL =========================

def calcular_precios_verticales(df_strikes, path_base, desplazamiento, desde, hasta):

    resultados = []
    symbol = df_strikes['Symbol'].iloc[0]

    df_strikes['Day'] = pd.to_datetime(df_strikes['Day'])
    df_strikes = df_strikes[(df_strikes['Day'] >= desde) & (df_strikes['Day'] <= hasta)].copy()

    df_strikes['Strike1_Desplazado'] = df_strikes.apply(
        lambda r: r['Strike1'] + desplazamiento if r['Option_Type'] == 'CALL' else r['Strike1'] - desplazamiento, axis=1
    )
    df_strikes['Strike2_Desplazado'] = df_strikes.apply(
        lambda r: r['Strike2'] + desplazamiento if r['Option_Type'] == 'CALL' else r['Strike2'] - desplazamiento, axis=1
    )

    for _, r in df_strikes.iterrows():

        fecha = r['Day'].strftime("%Y-%m-%d")
        hora = r['Hour']
        strike1 = r['Strike1_Desplazado']
        strike2 = r['Strike2_Desplazado']
        option_type = r['Option_Type']
        entry_time = pd.to_datetime(f"{r['Day']} {hora}")

        if symbol in ["SPX", "RUT", "XSP"]:
            archivo = f"{path_base}chains/optionChain_${symbol}_{fecha}.csv"
        else:
            archivo = f"{path_base}chains/optionChain_{symbol}_{fecha}.csv"

        credit, price_entry = calcular_credito_y_underlying_desde_archivo_csv(
            archivo, strike1, strike2, option_type, entry_time
        )

        close_price = obtener_ultimo_underlying_price(archivo)

        exit_reason = "CLOSE"
        exit_time = None
        pnl = None

        if USE_TP_SL and credit is not None:
            pnl, exit_reason, exit_time = simular_tp_sl(
                archivo, strike1, strike2, option_type, entry_time, credit, TP, SL
            )

        if pnl is None:
            spread = abs(strike1 - strike2)
            if option_type == "CALL":
                if close_price > max(strike1, strike2):
                    pnl = (credit - spread) * 100
                elif close_price < min(strike1, strike2):
                    pnl = credit * 100
                else:
                    pnl = credit * 100 - (close_price - min(strike1, strike2)) * 100
            else:
                if close_price < min(strike1, strike2):
                    pnl = (credit - spread) * 100
                elif close_price > max(strike1, strike2):
                    pnl = credit * 100
                else:
                    pnl = credit * 100 - (max(strike1, strike2) - close_price) * 100

        resultados.append({
            "Day": fecha,
            "Time": hora,
            "Strike1": strike1,
            "Strike2": strike2,
            "Option": option_type,
            "Credit": round(credit * 100, 2) if credit is not None else None,
            "EntryPrice": price_entry,
            "ClosePrice": close_price,
            "P/L": round(pnl, 2),
            "Exit": exit_reason,
            "ExitTime": exit_time
        })

    df = pd.DataFrame(resultados)
    return df, df['P/L'].sum(), (df['P/L'] > 0).sum(), (df['P/L'] < 0).sum()


# ========================= EJECUCIÓN =========================

df_strikes = pd.read_csv(elArchivo)

df, profit_total, wins, losses = calcular_precios_verticales(
    df_strikes, PATH_UBUNTU, desplazamiento, desde, hasta
)

df_console = df.copy()
df_console['P/L'] = df_console['P/L'].apply(colorize)

print(df_console.to_string(index=False))
print(f"\nProfit Total: ${profit_total:.2f}")
print(f"Wins: {wins} | Losses: {losses}")
print(f"Win Rate: {(wins/(wins+losses))*100:.2f}%")
print(f"TP: {TP} | SL: {SL}")

# ========================= OUTPUTS =========================

df.to_csv(
    f"resultados/P&L_{estrategia}_{symbol}_{timeHour}_{desde.replace('-','')}_{hasta.replace('-','')}_D{desplazamiento}_TPSL.csv",
    index=False
)
