#!/usr/bin/env python3
#encoding: UTF-8

import os
import sys
import mysql.connector
import pandas as pd
from datetime import datetime, timedelta
from decimal import Decimal
import numpy as np
import csv
from binance.client import Client

api_key = 'jqFSffA02ZaBlzrqRbvK9eIodOI912l3W8T9Cid9OGZuYw6JhyWQBbyhAyf2lfpC'
api_secret = 'iZ4ebjYhGhbbhfAm1D5Ys4VmSRtwRxwlcYawtu6ZaCxKAknzwsWOF3HiRuyYLIyB'

client = Client(api_key, api_secret)

def get_binance_trade_fee(symbol):
    fees = client.get_trade_fee(symbol=symbol)
    maker_fee = float(fees[0]['makerCommission'])
    taker_fee = float(fees[0]['takerCommission'])
    return maker_fee, taker_fee

start_date = str(sys.argv[1])[:10]
end_date = str(sys.argv[2])[:10]
symbol = str(sys.argv[3])
purchase_amount = float(sys.argv[4])
id_simul = int(sys.argv[5])


trailing_tp_active = False
peak_price = None

if "USDT" in symbol:
    symbolTo_trade = "USDT"
elif "USDC" in symbol:
    symbolTo_trade = "USDC"
elif "EUR" in symbol:
    symbolTo_trade = "EUR"
else:
    raise ValueError(f"Simbolo non riconosciuto: {symbol}")

if "USDT" in symbol or "USDC" in symbol:
    SPREAD_PERC = 0.0002
elif "EUR" in symbol:
    SPREAD_PERC = 0.0005

MAKER_FEE, TAKER_FEE = get_binance_trade_fee(symbol)

conn = mysql.connector.connect(
  host="localhost",
  user="adminsql",
  password="Esphere1662006",
  auth_plugin='mysql_native_password',
  database="cryptodata"
)
cursor = conn.cursor()

purchase_amount_iniziale = purchase_amount
saved_profit = 0.0
simulation_data = []
last_trade_quantity = 0.0

############################################
# Salvataggio CSV e caricamento dei dati  #
############################################

def save_to_csv():
    filename = f"/var/www/html/_log/bot_simulation_{symbol}_{start_date}_{end_date}.csv"
    with open(filename, mode='w', newline='') as file:
        writer = csv.writer(file)
        writer.writerow(["timestamp", "close_price", "action", "price", "quantity", "profit", "sma", "last_sell_price"])
        writer.writerows(simulation_data)
    print(f"Dati della simulazione salvati in: {filename}")

def savedata2db(id_simul, profit, trades_count):
    profit_perc = (profit / purchase_amount_iniziale) * 100 if purchase_amount_iniziale != 0 else 0.0
    query = '''\
    UPDATE simulazioni SET profit = %s, trades_count = %s, profit_percent = %s
    WHERE id = %s
    '''
    cursor.execute(query, (profit, trades_count, profit_perc, id_simul))
    conn.commit()
   
def load_historical_data(symbol, start_date, end_date):
    query = '''\
    SELECT timestamp, close FROM historical_data1sec_''' + symbol + '''
    WHERE timestamp BETWEEN %s AND %s
    ORDER BY timestamp ASC
    '''
    start_ts = start_date + " 00:00:00"
    end_ts   = end_date   + " 23:59:59"
    cursor.execute(query, (start_ts, end_ts))
    rows = cursor.fetchall()
    df = pd.DataFrame(rows, columns=['timestamp', 'close'])
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    df['close'] = df['close'].astype(float)
    return df

def load_historical_data_agg(symbol, start_date, end_date, aggregate_value):
    query = '''\
    SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(timestamp)/%s)*%s) as timestamp,
           AVG(close) as close
    FROM historical_data1sec_''' + symbol + '''
    WHERE timestamp BETWEEN %s AND %s
    GROUP BY FLOOR(UNIX_TIMESTAMP(timestamp)/%s)
    ORDER BY timestamp ASC
    '''
    start_ts = start_date + " 00:00:00"
    end_ts   = end_date   + " 23:59:59"
    params = (aggregate_value, aggregate_value, start_ts, end_ts, aggregate_value)
    cursor.execute(query, params)
    rows = cursor.fetchall()
    df = pd.DataFrame(rows, columns=['timestamp', 'close'])
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    df['close'] = df['close'].astype(float)
    return df

def load_historical_data_grouped(symbol, start_date, end_date, seconds):
    query = f'''
        SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(timestamp)/{seconds})*{seconds}) as timestamp,
               AVG(close) as close
        FROM historical_data1sec_{symbol}
        WHERE timestamp BETWEEN %s AND %s
        GROUP BY FLOOR(UNIX_TIMESTAMP(timestamp)/{seconds})
        ORDER BY timestamp ASC
    '''
    start_ts = start_date + " 00:00:00"
    end_ts   = end_date   + " 23:59:59"
    cursor.execute(query, (start_ts, end_ts))
    rows = cursor.fetchall()
    df = pd.DataFrame(rows, columns=['timestamp', 'close'])
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    df['close'] = df['close'].astype(float)
    return df

############################################
# Log nel DB MySQL delle azioni del bot   #
############################################

def log_bot_action(action_type, timestamp, price, quantity, id_simul, profit=None):
    timestamp_str = timestamp.strftime('%Y-%m-%d %H:%M:%S')
    cursor.execute('''\
    INSERT INTO bot_actions (action_type, timestamp, symbol, price, quantity, id_simul, profit)
    VALUES (%s, %s, %s, %s, %s, %s, %s)
    ''', (action_type, timestamp_str, symbol, price, quantity, id_simul, profit))
    conn.commit()

############################################
# Funzioni di acquisto e vendita         #
############################################


def execute_buy_simulation(price, usdt_amount):
    price_with_spread = price * (1 + SPREAD_PERC)  # simula acquisto al prezzo Ask
    quantity_bought = usdt_amount / price_with_spread
    commission_amount = quantity_bought * MAKER_FEE
    net_quantity = quantity_bought - commission_amount
    return net_quantity, price_with_spread

def execute_sell_simulation(amount, price):
    price_with_spread = price * (1 - SPREAD_PERC)  # simula vendita al prezzo Bid
    usdt_received = amount * price_with_spread
    commission_amount = usdt_received * TAKER_FEE
    net_amount_received = usdt_received - commission_amount
    return net_amount_received

############################################
# Funzioni di analisi dei dati         #
############################################



############################################
# Parametri e funzioni di strategia      #
############################################

TAKE_PROFIT_PERCENTAGE = 0.01 # 2%
STOP_LOSS_PERCENTAGE = 0.02 # 0.02 1%
COOLDOWN_BARS = 15
RETRACE_PERC = 0.008 # 0.008
SMOOTH_WINDOW  = 12
XSMOOTH_WINDOWSH = 20
XSMOOTH_WINDOW = 50
KXSM = 15 #moltiplicatore per il trend 

AGGREGATE_DATA = True  # Se True, usa i dati aggregati a 5 secondi
AGGREGATE_VALUES = 10

LEVERAGE = 1  # leva x1


##############################################################
#                                                            #
#                                                            #
##############################################################
def media_drawdown(media_history, window=20):
    """
    Restituisce il drawdown percentuale della media mobile negli ultimi `window` punti.
    """
    if len(media_history) < window:
        return 0.0
    segment = media_history[-window:]
    max_val = max(segment)
    min_val = min(segment)
    if max_val == 0:
        return 0.0
    return (max_val - min_val) / max_val  # drawdown %


def has_significant_minimum(media_segment, min_diff_perc=0.002, epsilon=0.0005):
    """
    Restituisce True se nel segmento c'è un minimo significativo:
    - Il minimo locale non è agli estremi
    - La differenza tra il minimo e i massimi a sinistra e destra è almeno min_diff_perc (es: 0.002 = 0.2%)
    - Le micro oscillazioni sono smorzate tramite epsilon
    """
    window = len(media_segment)
    if window < 5:
        return False

    # Smorzamento oscillazioni
    smoothed = [media_segment[0]]
    for v in media_segment[1:]:
        if abs(v - smoothed[-1]) > epsilon:
            smoothed.append(v)
        else:
            smoothed.append(smoothed[-1])

    min_idx = smoothed.index(min(smoothed))
    if min_idx == 0 or min_idx == window - 1:
        return False

    left_max = max(smoothed[:min_idx])
    right_max = max(smoothed[min_idx+1:])

    min_val = smoothed[min_idx]
    diff_left = (left_max - min_val) / left_max if left_max != 0 else 0
    diff_right = (right_max - min_val) / right_max if right_max != 0 else 0

    return diff_left >= min_diff_perc and diff_right >= min_diff_perc

def is_significant_minimum_on_media_delayed(media_history, window=20, min_drawdown=0.00075, candidate_offset=4):
    """
    Rileva un minimo significativo sulla media mobile con controllo della derivata (recovery) dopo il minimo.
    - candidate_offset: quanti punti prima della fine della finestra considerare come minimo candidato (es: 3 -> terzultimo)
    - recovery_points: quanti punti dopo il minimo usare per calcolare la derivata media
    - min_recovery_slope: valore minimo della derivata media per considerare valido il recovery
    """
    global drawdown, avg_recovery_slope

    if len(media_history) < window:
        return False

    segment = media_history[-window:]
    candidate_idx = -candidate_offset
    candidate = segment[candidate_idx]
    before = segment[:candidate_idx]
    after = segment[candidate_idx+1:]

    recovery_points = candidate_offset - 1 # quanti punti dopo il minimo usare per calcolare la derivata media
    min_recovery_slope=0.0#0.7#1.0

    is_local_min = candidate < min(before + after)
    drawdown = (max(segment) - min(segment)) / max(segment)

    # Calcola la derivata media sui recovery_points successivi
    recovery = False
    if len(after) >= recovery_points:
        recovery_slopes = [after[i+1] - after[i] for i in range(recovery_points-1)]
        avg_recovery_slope = sum(recovery_slopes) / len(recovery_slopes)
        recovery = avg_recovery_slope > min_recovery_slope
    # fallback: se non ci sono abbastanza punti, usa il vecchio controllo
    else:
        recovery = all(after[i+1] > after[i] for i in range(len(after)-1))

    return is_local_min and recovery and drawdown > min_drawdown 

def is_significant_minimum_on_media_delayed_OLD(media_history, window=20, min_drawdown=0.00075):
    if len(media_history) < window:
        return False

    segment = media_history[-window:]
    candidate = segment[-3]
    before = segment[:-3]
    after = segment[-2:]

    is_local_min = candidate < min(before + after)
    drawdown = (max(segment) - min(segment)) / max(segment)

    recovery = after[0] > candidate and after[1] > after[0]

    if is_local_min and recovery:
        print(f"[DEBUG] Local min detected: drawdown {drawdown:.5f} ")

    return is_local_min and recovery and drawdown > min_drawdown 




def check_is_long_min(short_media_history, window=120, min_drawdown=0.002):

    if len(short_media_history) < window:
        return False

    segment = short_media_history[-window:]
    min_val = min(segment)
    latest = segment[-1]

    has_min = has_significant_minimum(segment, min_diff_perc=0.001, epsilon=0.0005)
    if latest > min_val and (latest - min_val) / min_val > min_drawdown and has_min:
        return True
    return False

def simulate_bot_hybrid_real(state, df):
    global simulation_data, id_simul, purchase_amount, saved_profit  # Rendi purchase_amount modificabile
    global TAKE_PROFIT_PERCENTAGE, STOP_LOSS_PERCENTAGE, COOLDOWN_BARS, XSMOOTH_WINDOW
    global price_history, smoothed_history, short_media_history, media_history, trend_history, last_trade_quantity
    global trend_derivative, drawdown

    trade_active       = state['trade_active']
    buy_price          = state['buy_price']
    trade_quantity     = state['trade_quantity']
    total_profit       = state['total_profit']
    trades_count       = state['trades_count']
    cooldown_counter   = state['cooldown_counter']
    last_sell_price    = state['last_sell_price']
    max_price          = state['max_price']
    max_smprice        = state['max_smprice']
    trailing_tp_active = state['trailing_tp_active']
    peak_price         = state['peak_price']
    forcebuy_locked    = state['forcebuy_locked']
    exit_stop_loss     = state['exit_stop_loss']
    last_media_derivative = state['last_media_derivative']
    forcebuycounter    = state['forcebuycounter']
    trailing_down_active = state['trailing_down_active']

    

    buf = []
    append = True
    count = 0

    for i, row in df.iterrows():
        price1sec = float(row['close'])
        timestamp = row['timestamp']



        buf.append(price1sec)
        if len(buf) == buffer_length:                     
            price = sum(buf)/len(buf)  #  buf[-1] # 
            buf.clear()
            append = True
        else:
            continue
            if not trade_active:
                continue
            else:
                append = False
                price = price1sec  # Mantieni il prezzo corrente se non si è raggiunta la lunghezza del buffer
                smoothed_val = np.mean(smoothed_history[-1]) if smoothed_history else price1sec
                media_val = media_history[-1] if media_history else price1sec
                # ...dove chiami la funzione di minimo locale...
        

        if append:
            price_history.append(price)
            if len(price_history) > SMOOTH_WINDOW*XSMOOTH_WINDOW*KXSM:
                price_history = price_history[-SMOOTH_WINDOW*XSMOOTH_WINDOW*KXSM:]

            smoothed_val = price
            media_val = price
            trend_val = price
            short_media_val = price

            if len(price_history) >= SMOOTH_WINDOW:
                smoothed_val = np.mean(price_history[-SMOOTH_WINDOW:])
            if len(price_history) >= SMOOTH_WINDOW*XSMOOTH_WINDOWSH:
                short_media_val = np.mean(price_history[-SMOOTH_WINDOW*XSMOOTH_WINDOWSH:])
            if len(price_history) >= SMOOTH_WINDOW*XSMOOTH_WINDOW:
                media_val = np.mean(price_history[-SMOOTH_WINDOW*XSMOOTH_WINDOW:])
                #trend_val = media_val
            if len(price_history) >= SMOOTH_WINDOW*XSMOOTH_WINDOW*KXSM:
                trend_val = np.mean(price_history[-SMOOTH_WINDOW*XSMOOTH_WINDOW*KXSM:])
            else:
                trend_val = np.mean(price_history)

            media_history.append(media_val)
            if len(media_history) > 500:
                media_history = media_history[-500:]

            smoothed_history.append(smoothed_val)
            if len(smoothed_history) > 1000:
                smoothed_history = smoothed_history[-1000:]
            
            trend_history.append(trend_val)
            if len(trend_history) > 50:
                trend_history = trend_history[-50:]

            short_media_history.append(short_media_val)
            if len(short_media_history) > 200:
                short_media_history = short_media_history[-200:]

            #trend_derivative = np.diff(trend_history)
            last_trend_derivative = 0
            if len(trend_history) > 2:
                last_trend_derivative = trend_history[-1]-trend_history[-2]

            trend_derivative.append(last_trend_derivative)
            if len(trend_derivative) > 500:
                trend_derivative = trend_derivative[-500:]

            media_derivative = np.diff(media_history)
            last_media_derivative = media_derivative[-1] if len(media_derivative) > 0 else 0.0

            short_media_derivative = np.diff(short_media_history)
            last_short_media_derivative = short_media_derivative[-1] if len(short_media_derivative) > 0 else 0.0

            min_drawdown=0.0020

            STOP_LOSS_PERCENTAGE = 0.02
            if len(price_history) >= SMOOTH_WINDOW*XSMOOTH_WINDOW*KXSM:
                if last_trend_derivative >= 0.2:#and last_media_derivative >= 0
                    STOP_LOSS_PERCENTAGE = 0.02 # 2%
                    #XSMOOTH_WINDOW = 50
                else:
                    STOP_LOSS_PERCENTAGE = 0.01 # 1%
                    #XSMOOTH_WINDOW = 30
            
            

            #if(smoothed_val < media_val):
            #    STOP_LOSS_PERCENTAGE = 0.01

            retracesm_perc = 0.0
            max_smprice = 0.0
            if len(smoothed_history) > 60:
                max_smprice = max(smoothed_history[-60:])
                #if max_smprice is None or smoothed_val > max_smprice:
                #    max_smprice = smoothed_val
            elif len(smoothed_history) <= 60:
                max_smprice = max(smoothed_history)
            if max_smprice > 0:
                retracesm_perc = (max_smprice - smoothed_val) / max_smprice

            condition_met = False
            forceBuy = False

            if exit_stop_loss:
                media_interval = 200
            else:
                media_interval = 100


            if len(trend_derivative) > media_interval:
                recent_values = trend_derivative[-media_interval:]  # -len(media_derivative)//2 Prendi metà dei valori più recenti
                positive_count = sum(1 for val in recent_values if val > 0)#0.8 - 0.9 OK per
                if positive_count >= len(recent_values) : #or last_media_derivative>3.5
                    condition_met = True
                else:
                    condition_met = False
            else:
                condition_met = False

            smooth_derivative = np.diff(smoothed_history)
            last_smooth_derivative = smooth_derivative[-1] if len(smooth_derivative) > 0 else 0.0
            if(last_smooth_derivative > 36 and last_short_media_derivative > 9  and len(price_history) >= SMOOTH_WINDOW*XSMOOTH_WINDOW):
            #if(last_smooth_derivative > 36 and last_short_media_derivative > 9 and len(price_history) >= SMOOTH_WINDOW*XSMOOTH_WINDOW):and last_trend_derivative > 0.4
                if not forcebuy_locked:
                    forceBuy = True
                    forcebuycounter = COOLDOWN_BARS * 200   

            if( not trailing_down_active and smoothed_val < short_media_val and (short_media_val - smoothed_val) / short_media_val > 0.04):
                trailing_down_active = True
                price_down_rif = smoothed_val
                #print(f"[DEBUG] Trailing down active: {smoothed_val:.4f} < {short_media_val:.4f} ")
            elif(trailing_down_active):
                if smoothed_val < price_down_rif :  # 
                    price_down_rif = smoothed_val
                if smoothed_val > price_down_rif * 1.002:  # sale dello 0.2% dal massimo
                    forceBuy = True
                    trailing_down_active = False
                

            is_local_min = False
            if not trade_active:
                #is_local_min = is_significant_minimum_on_media_delayed(media_history, window=60, min_drawdown=min_drawdown)
                is_local_min = is_significant_minimum_on_media_delayed(short_media_history, window=60, min_drawdown=min_drawdown, candidate_offset=4)
                
                is_long_min = False#check_is_long_min(short_media_history, window=120, min_drawdown=min_drawdown)

            ts = timestamp
            if isinstance(ts, pd.Timestamp):
                ts = ts.to_pydatetime()
            price = float(price)
            smoothed_val = float(smoothed_val)
            #media_d = float(last_media_derivative)
            media_d  = float(media_val)
            media  = float(trend_val)
            short_media = float(short_media_val)
            cursor.execute('''
                INSERT INTO sim_derivatives (id_simul, timestamp, price, smoothed, media_val, trend_val, short_media_val)
                VALUES (%s, %s, %s, %s, %s, %s, %s)
            ''', (id_simul, ts, price, smoothed_val, media_d, media, short_media))
            #conn.commit()

            count += 1
            if count % 500 == 0:
                conn.commit()
                count = 0

            # Gestione del cooldown
            if cooldown_counter > 0:
                cooldown_counter -= 1
            #if forcebuy_locked:
            #    forcebuycounter -= 1
            #    if forcebuycounter <= 0:
            #        forcebuy_locked = False
            #        forcebuycounter = 0

        ##trade_active = False
        # Logica di vendita
        if trade_active:
            # Take profit
            #if not trailing_tp_active and price >= buy_price * (1 + TAKE_PROFIT_PERCENTAGE): #A
            if not trailing_tp_active and smoothed_val >= buy_price * (1 + TAKE_PROFIT_PERCENTAGE): #B
                trailing_tp_active = True
                peak_price = smoothed_val
                #print(f"[DEBUG] peak_price: {peak_price} ")
            elif trailing_tp_active:
                pricerif = smoothed_val
                #diff = ((pricerif - peak_price * 0.998)/peak_price) * 100
                #print(f"[DEBUG] price - peak_price:  {diff} ")
                #print(f"[DEBUG] price  - peak_price: {pricerif} - {peak_price} ")
                if pricerif > peak_price:
                    peak_price = pricerif  # aggiorna il massimo
                elif pricerif < peak_price * 0.998:  # soglia di uscita: scende dello 0.2% dal massimo
                    # VENDI
                    trailing_tp_active = False
            
                    net_usdt_received = execute_sell_simulation(trade_quantity, price)
                    profit = net_usdt_received - purchase_amount
                    total_profit += profit
                    trades_count += 1

                    # Aggiorna purchase_amount con il profitto
                    purchase_amount += profit/2
                    saved_profit += profit/2

                    log_bot_action('sell', timestamp, price, trade_quantity, id_simul, profit)
                    simulation_data.append([timestamp, price, "SELL", price, trade_quantity, profit, "", last_sell_price])
                    print(f"[{timestamp}] SELL (TP): {trade_quantity:.8f} @ {price:.8f} {symbolTo_trade} (profit: {profit:.4f})")

                    last_sell_price = price
                    
                    trade_active = False
                    cooldown_counter = COOLDOWN_BARS
                    trade_quantity = 0.0
                    buy_price = 0.0
                    max_price = price


            # Stop loss
            #elif price <= buy_price * (1 - STOP_LOSS_PERCENTAGE) :
            elif price <= buy_price * (1 - STOP_LOSS_PERCENTAGE) :
                net_usdt_received = execute_sell_simulation(trade_quantity, price)
                loss = net_usdt_received - purchase_amount
                total_profit += loss
                trades_count += 1

                STOP_LOSS_CALC = (price - buy_price) / buy_price
                print(f"[DEBUG] STOP_LOSS_PERCENTAGE: {STOP_LOSS_CALC} ")

                # Aggiorna purchase_amount con la perdita
                purchase_amount += loss  # Nota: `loss` è negativo, quindi riduce purchase_amount

                log_bot_action('stop_loss', timestamp, price, trade_quantity, id_simul, loss)
                simulation_data.append([timestamp, price, "STOP_LOSS", price, trade_quantity, loss, "", last_sell_price])
                print(f"[{timestamp}] STOP_LOSS: {trade_quantity:.8f} @ {price:.8f} [{last_trend_derivative:.4f}][{last_media_derivative:.4f}] (loss: {loss:.4f})")

                last_sell_price = price
                trade_active = False
                cooldown_counter = COOLDOWN_BARS * 10
                trade_quantity = 0.0
                buy_price = 0.0
                max_price = price
                exit_stop_loss = True
                forcebuy_locked = True
                

            else:
                simulation_data.append([timestamp, price, "HOLD", "", "", "", "", last_sell_price])

        # Logica di acquisto
        #elif cooldown_counter == 0 and ((is_local_min  and retracesm_perc >= RETRACE_PERC) or forceBuy):
        elif cooldown_counter == 0 and ((is_local_min ) or forceBuy or is_long_min):#and condition_met
            #if True or retracesm_perc >= 0.002: #last_sell_price is None or price < last_sell_price:
            #if last_sell_price is None or price < last_sell_price or not Wait_for_price_down_sl:
            if True:
                # Aumenta la dimensione della posizione
                leveraged_amount = purchase_amount * LEVERAGE
                trade_quantity, buy_price = execute_buy_simulation(price, leveraged_amount)
                trade_active = True
                trades_count += 1
                #last_max_price = price
                max_smprice = price
                exit_stop_loss = False
                last_trade_quantity = purchase_amount

                act = "buy"
                if forceBuy:
                    act = "forced_buy"
                    forcebuy_locked = True
                    print(f"[DEBUG] FORCED BUY: {last_smooth_derivative:.4f} - {last_media_derivative:.4f}" )
                else:
                    forcebuy_locked = False
                log_bot_action(act, timestamp, price, trade_quantity, id_simul)
                simulation_data.append([timestamp, price, act, price, trade_quantity, "", "", last_sell_price])
                print(f"[{timestamp}] {act.upper()}: {purchase_amount:.4f}/{trade_quantity:.8f} @ {price:.8f} {symbolTo_trade}")
                print(f"{act.upper()}: {min_drawdown:.8f} {drawdown:.4f} {condition_met} avg_recovery_slope: {avg_recovery_slope:.6f} {last_trend_derivative:.4f} {last_media_derivative:.4f}")
                print(f"[DEBUG] last_media_derivative: {last_media_derivative}")
                max_price = price
                forceBuy = False
        else:
            simulation_data.append([timestamp, price, "HOLD", "", "", "", "", last_sell_price])

    # Aggiorna lo state con i valori modificati
    state['trade_active']         = trade_active
    state['buy_price']            = buy_price
    state['trade_quantity']       = trade_quantity
    state['total_profit']         = total_profit
    state['trades_count']         = trades_count
    state['cooldown_counter']     = cooldown_counter
    state['last_sell_price']      = last_sell_price
    state['max_price']            = max_price
    state['max_smprice']          = max_smprice
    state['trailing_tp_active']   = trailing_tp_active
    state['peak_price']           = peak_price
    state['forcebuy_locked']      = forcebuy_locked
    state['exit_stop_loss']       = exit_stop_loss
    state['last_media_derivative'] = last_media_derivative
    state['forcebuycounter']      = forcebuycounter
    state['trailing_down_active'] = trailing_down_active

    return state




############################################
# Inizio simulazione                       #
# Pulizia dati precedenti
query = "DELETE FROM bot_actions WHERE id_simul = %s"
cursor.execute(query, (id_simul,))
conn.commit()

query = "DELETE FROM sim_derivatives WHERE id_simul = %s"
cursor.execute(query, (id_simul,))
conn.commit()

params = "TAKE_PROFIT_PERCENTAGE=%s, STOP_LOSS_PERCENTAGE=%s, COOLDOWN_BARS=%s"
params = params % (
    str(TAKE_PROFIT_PERCENTAGE), 
    str(STOP_LOSS_PERCENTAGE), 
    str(COOLDOWN_BARS) 
)
query = "UPDATE simulazioni SET params=%s WHERE id = %s"
cursor.execute(query, (params, id_simul))
conn.commit()

simulation_state = {
    'trade_active': False,
    'buy_price': 0.0,
    'trade_quantity': 0.0,
    'total_profit': 0.0,
    'trades_count': 0,
    'cooldown_counter': 0,
    'last_sell_price': None,
    'max_price': None,
    'max_smprice': None,
    'trailing_tp_active': False,
    'peak_price': None,
    'forcebuy_locked': False,
    'exit_stop_loss': False,
    'last_media_derivative': None,
    'forcebuycounter': 0,
    'trailing_down_active': False
}

price_history = []
smoothed_history = []
short_media_history = []
media_history = []
trend_history = []
media_derivative = []
trend_derivative = []
buffer_length = 60
forcebuycounter = 0
drawdown = 0.0
avg_recovery_slope = 0.0

# Converte le date in oggetti datetime
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
end_dt   = datetime.strptime(end_date, "%Y-%m-%d")

current_day = start_dt
while current_day <= end_dt:
    next_day = current_day + timedelta(days=1)
    day_start = current_day.strftime("%Y-%m-%d")
    #day_end   = next_day.strftime("%Y-%m-%d")
    day_end = current_day.strftime("%Y-%m-%d")

    plen = len(price_history)
    print(f"Elaborazione dei dati per il giorno: {day_start} - {day_end} (lunghezza buffer: {plen})")
    
    if AGGREGATE_DATA:
        # poiché ogni record ora rappresenta aggregate_value secondi,
        # il buffer per avere 60 secondi diventa 60 / AGGREGATE_VALUES
        buffer_length = int(60 / AGGREGATE_VALUES)
        df = load_historical_data_grouped(symbol, day_start, day_end, AGGREGATE_VALUES)
    else:
        buffer_length = 60
        df = load_historical_data(symbol, day_start, day_end)
    simulation_state = simulate_bot_hybrid_real(simulation_state, df)

    current_day = next_day

# Fine loop, stampa riepilogo
total = (last_trade_quantity - purchase_amount_iniziale) + saved_profit
print("\n=== RIEPILOGO IBRIDA + SL ===")
print(f"Numero trades: {simulation_state['trades_count']} ")
print(f"Profit totale: {total:.8f} {symbolTo_trade}")
print(f"Accumulato: {saved_profit:.8f} Last amount: {last_trade_quantity:.8f}")
print(f"{last_trade_quantity:.8f} {purchase_amount_iniziale:.8f}")
# save_to_csv()
savedata2db(id_simul, total, simulation_state['trades_count'])

cursor.close()
conn.close()
