Novità

ICQ — INDICE DI CLIMA DELLA CASUALITÀ Una nuova prospettiva per studiare il Lotto di Alessandro Ginestrino

Ho letto tutto con molto interesse, anche se mi interesso di entropia, nel campo morfogenetico soprattutto applicato alla teoria della risonanza morfica di Rupert Sheldrake dove diventa sintropia e si comporta in modo diametralmente opposto rispetto alla seconda legge della termodinamica , mi affascina l'entropia applicata alle dinamiche del lotto .... purtroppo non ho le vostre capacità di addentrarmi negli script... però ho altre conoscenze relative a come solo osservando un fenomeno lo modifico ( Fisica Quantistica ) e non solo perchè fisicamente un interazione tra una particella e un fotone modifica il fenomeno che si stava osservando, ma anche perchè se in più persone stiamo osservando , i nostri campi morfogenetici interagiscono tra di loro senza che noi si sia consci di questo .... questo ha come postulato che se andiamo ad analizzare una formazione numerica , il solo pensiero che la formazione si sfaldi, porta allo sfaldamento della stessa .... so che è abbastanza incredibile da accettare, ma le recenti scoperte, basti pensare alla "SONOCITOLOGIA E LE MELODIE CELLULARI" di Carlo Ventura, portano verso una nuova visione della "Materia che Danza" ... non mi dilungo oltre ..cmq per quel che riguarda la previsione su Roma, che mi piace molto, se posso la vedrei bene
su ROMA E GENOVA PER TERNO
SU VE GE PER AMBO ED ESTRATTO quindi dato che GE mi compare come top da estratto a terno
OLTRE CHE A ROMA LA METTEREI IN GIOCO ANCHE SU GENOVA
chiedo venia per l'intervento tra voi tecnici , il mio lotto è poetico e scientifico allo stesso tempo, voi siete molto più rigorosi scientificamente parlando
buona continuazione
 
Ciao Alessandro complimenti. Stò cercando di riprodurre la tua previsione usando le tue informazioni
entropia 40% + ripetizioni 30% + aggregazione 30% sulla finestra di 20 concorsi, ma ricevo risultati diversi al 16/06/2026.

DISTRIBUZIONE STATI
Fredda 0.0%
Neutra 29.0%
Attiva 70.0%
Calda 0.8%
Turbolenta 0.2%

CLASSIFICA RUOTE
1. BA Bari ICQ 61.3 Attiva run=49 trend +5.9 score 45.6
2. NA Napoli ICQ 56.6 Attiva run=25 trend +7.4 score 45.1
3. TO Torino ICQ 55.3 Attiva run=24 trend +4.9 score 40.4
4. MI Milano ICQ 66.2 Attiva run=7 trend -0.2 score 39.5
5. RN Nazionale ICQ 64.0 Attiva run=22 trend -1.0 score 36.9
6. GE Genova ICQ 60.4 Attiva run=20 trend -4.9 score 28.9
7. FI Firenze ICQ 53.4 Attiva run=22 trend -3.6 score 26.7
8. VE Venezia ICQ 45.8 Attiva run=7 trend -10.3 score 20.0
9. PA Palermo ICQ 36.1 Neutra run=2 trend -0.9 score 15.2
10. RM Roma ICQ 20.2 Neutra run=3 trend -0.5 score 6.4
11. CA Cagliari ICQ 26.2 Neutra run=9 trend -4.5 score 4.0

DETTAGLIO RUOTA TOP: BA Bari - stato Attiva ICQ 61.3
t+1: Neutra 7.7%, Attiva 91.7%, Calda 0.5%
t+2: Neutra 13.4%, Attiva 85.8%, Calda 0.7%
t+3: Neutra 17.5%, Attiva 81.5%, Calda 0.8%, Turbolenta 0.1%
TOP 20 numeri: 71(78) 62(76) 86(74) 25(69) 51(64) 28(63) 76(60) 6(60) 64(59) 23(59) 81(58) 19(58) 78(58) 83(58) 72(57) 74(56) 88(56) 18(56) 84(55) 69(55)

Se ti va di condividere questa informazione, potresti dirmi i valori grezzi delle tre componenti (entropia, ripetizioni, aggregazione) prima della normalizzazione, e l'ICQ finale. Inoltre come passi dalle tre componenti pesate (40/30/30) al valore 0–100: scala assoluta con limiti fissi, z-score o percentile? La normalizzazione è per singola ruota o globale? Su quale periodo di riferimento (tutto il 1939–2026)?

Comunque la previsione sopra, seppur sbagliata, ha dato il 62 a BA al primo colpo, fortuna ?

Grazie
 
"""
╔══════════════════════════════════════════════════════════════════╗
║ ICQ PREDITTORE — Analisi del Caos e Modello Predittivo Lotto ║
║ Alessandro Ginestrino ║
║ v2.0 — Software completo ║
╚══════════════════════════════════════════════════════════════════╝

ALGORITMI IMPLEMENTATI:
1. ICQ per ruota — Indice di Clima della Casualità rolling per
ciascuna delle 11 ruote, composto da:
entropia locale (40%), ripetizioni (30%),
aggregazione numerica (30%)
2. Score composito — ICQ(35%) + Trend(25%) + Ritardo(20%) +
Ripetizione(20%) → classifica ruote
3. Selezione numeri — Score per ogni num su 4 filtri sovrapposti:
ritardo ottimale, fascia ICQ, storico Calda,
attività recente
4. Co-occorrenza Calda — coppie uscite insieme in fasi Calde
5. Timing predittivo — run-length, pressione inversione,
probabilità transizione t+1/t+2/t+3
6. Riepilogo operativo — sintesi finale con segnali d'azione
"""

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from tkinter import font as tkfont
import pandas as pd
import numpy as np
import threading
from datetime import datetime, date
import warnings
warnings.filterwarnings('ignore')

# ═══════════════════════════════════════════════════════════════════
# PARAMETRI
# ═══════════════════════════════════════════════════════════════════
WINDOW_ICQ = 20
PESO_ICQ, PESO_TREND, PESO_RITARDO, PESO_RIPET = 0.35, 0.25, 0.20, 0.20

RUOTE_NOME = {
'BA':'Bari','CA':'Cagliari','FI':'Firenze','GE':'Genova',
'MI':'Milano','NA':'Napoli','PA':'Palermo','RM':'Roma',
'RN':'Nazionale','TO':'Torino','VE':'Venezia'
}

# Colori (sfondo bianco, caratteri leggibili)
BG = '#FFFFFF'
BG2 = '#F4F6FB'
BG3 = '#E8ECF4'
BG4 = '#DDE2EE'
BORDER = '#C8CFDF'
TEXT = '#111827'
TEXT2 = '#4B5568'
TEXT3 = '#9CA3AF'
ACCENT = '#1D4ED8'
ACC2 = '#2563EB'
VERDE = '#059669'
ROSSO = '#DC2626'
ARANCIO = '#D97706'
CIELO = '#0284C7'
VIOLA = '#7C3AED'

# Colori per stati ICQ
COL_ST = {0:'#0369A1', 1:'#64748B', 2:'#0891B2', 3:'#EA580C', 4:'#B91C1C'}
NOM_ST = {0:'Fredda', 1:'Neutra', 2:'Attiva', 3:'Calda', 4:'Turbolenta'}
EMO_ST = {0:'❄', 1:'◌', 2:'◎', 3:'🔥', 4:'⚡'}

# Font
FM = ('Helvetica', 10)
FB = ('Helvetica', 10, 'bold')
FT = ('Helvetica', 12, 'bold')
FH = ('Helvetica', 14, 'bold')
FH2 = ('Helvetica', 16, 'bold')
FMO = ('Courier', 10)
FSM = ('Helvetica', 9)
FSB = ('Helvetica', 9, 'bold')

# ═══════════════════════════════════════════════════════════════════
# MOTORE DI ANALISI
# ═══════════════════════════════════════════════════════════════════
class MotoreICQ:
def __init__(self):
self.df_raw = None
self.risultati = {}

# ── caricamento ──────────────────────────────────────────────
def carica(self, path):
df = pd.read_csv(path, sep='\t', header=None,
names=['data','ruota','n1','n2','n3','n4','n5'],
keep_default_na=False)
df['data'] = pd.to_datetime(df['data'])
df = df.sort_values(['data','ruota']).reset_index(drop=True)
self.df_raw = df
return df['data'].max().date()

# ── elaborazione principale ───────────────────────────────────
def elabora(self, data_fine_str, cb=None):
df = self.df_raw[self.df_raw['data'] <= pd.to_datetime(data_fine_str)].copy()
ruote = sorted(df['ruota'].unique())
self.risultati = {}
for i, r in enumerate(ruote):
if cb: cb(i, len(ruote), r)
sub = df[df['ruota']==r].reset_index(drop=True)
res = self._analizza(sub, r)
if res:
self.risultati[r] = res
if cb: cb(len(ruote), len(ruote), 'OK')
return self.risultati

# ── analisi singola ruota ─────────────────────────────────────
def _analizza(self, sub, ruota):
nums = [sub.loc[i,['n1','n2','n3','n4','n5']].values.astype(int)
for i in range(len(sub))]
n = len(nums)
if n < WINDOW_ICQ + 10:
return None

# Serie ICQ
E, R, A = [], [], []
for i in range(WINDOW_ICQ, n):
fin = np.concatenate(nums[i-WINDOW_ICQ:i])
E.append(self._ent(fin))
R.append(self._rip(nums, nums[i-1]))
A.append(self._agg(nums))
E, R, A = np.array(E), np.array(R), np.array(A)

def n01(x):
d = x.max()-x.min()
return (x-x.min())/d if d > 0 else np.zeros_like(x)

ICQ = n01(n01(E)*40 + n01(R)*30 + n01(A)*30) * 100
stati = np.array([self._cls(v) for v in ICQ])

# Stato e run corrente
s = stati[-1]
run = 1
for k in range(len(stati)-2, -1, -1):
if stati[k]==s: run += 1
else: break

# Trend ICQ (pendenza lineare ultimi 8)
win_t = min(8, len(ICQ))
trend = float(np.polyfit(range(win_t), ICQ[-win_t:], 1)[0])

# Score composito ruota
icq_n = ICQ[-1]/100
trend_n = max(0, min(1, (trend+5)/10))
rit_s = self._rit_score(nums)
rep_s = self._rep_score(nums[-12:])
score = (icq_n*PESO_ICQ + trend_n*PESO_TREND +
rit_s*PESO_RITARDO + rep_s*PESO_RIPET)*100

# Probabilità transizioni (da dati storici ruota)
prob = self._prob_trans(stati, s)
# Proiezioni t+2, t+3 (propagazione Markov)
prob2 = self._markov_step(stati, prob)
prob3 = self._markov_step(stati, prob2)

# Pressione inversione
press = self._pressione(s, run)

# Run distribution storica
rd = self._run_dist(stati, s)

# Selezione numeri
numeri = self._seleziona(nums, stati, s)

# Coppie in Calda
coppie = self._coppie_calda(nums, stati)

# Ritardi attuali di tutti i numeri
ritardi = self._ritardi_tutti(nums, n)

# Statistiche per stato (ANOVA-like: medie numeriche per clima)
stats_per_stato = self._stats_per_stato(nums, stati)

# Cronologia ultimi 20 ICQ con date
date_arr = sub['data'].values[WINDOW_ICQ:]
cronologia = [(pd.Timestamp(date_arr).strftime('%d/%m/%y'),
float(ICQ), int(stati))
for i in range(max(0,len(ICQ)-20), len(ICQ))]

return dict(
ruota=ruota, nome=RUOTE_NOME.get(ruota,ruota),
icq=float(ICQ[-1]), icq_arr=ICQ, stati=stati,
stato=s, stato_nome=NOM_ST, run=run, trend=trend,
score=float(score), prob=prob, prob2=prob2, prob3=prob3,
pressione=float(press), run_dist=rd,
numeri=numeri, coppie=coppie, ritardi=ritardi,
stats_stato=stats_per_stato, cronologia=cronologia,
n_est=n, n_calda=int((stati==3).sum()),
n_turb=int((stati==4).sum()),
)

# ── indicatori ───────────────────────────────────────────────
def _ent(self, arr):
c = np.bincount(arr, minlength=91)[1:]
p = c/c.sum(); p = p[p>0]
return -np.sum(p*np.log2(p))

def _rip(self, cur, prv):
return sum(1 for x in cur if x in set(prv))/len(cur)

def _agg(self, arr):
s = sorted(arr)
return 1 - np.mean([s[i+1]-s for i in range(len(s)-1)])/89

def _cls(self, v):
if v<20: return 0
if v<40: return 1
if v<70: return 2
if v<85: return 3
return 4

# ── score componenti ─────────────────────────────────────────
def _rit_score(self, nums):
n = len(nums)
last = {}
for i,arr in enumerate(nums):
for x in arr: last[x] = i
rits = [n - last.get(x,-1) - 1 for x in range(1,91)]
media = np.mean(rits)
ottimi = sum(1 for r in rits if r <= media*2)
return ottimi/90

def _rep_score(self, ultimi):
if len(ultimi)<2: return 0.5
c = np.bincount(np.concatenate(ultimi), minlength=91)[1:]
return min(1.0, float(np.sum(c>=2))/90 * 3)

# ── probabilità e Markov ──────────────────────────────────────
def _prob_trans(self, stati, s_cur):
trans = np.zeros((5,5))
for t in range(len(stati)-1):
trans[stati[t],stati[t+1]] += 1
row = trans[s_cur]; tot = row.sum()
if tot == 0:
return {i:0.2 for i in range(5)}
return {i:float(row/tot) for i in range(5)}

def _markov_step(self, stati, prob_in):
# Calcola matrice di transizione globale
T = np.zeros((5,5))
for t in range(len(stati)-1):
T[stati[t],stati[t+1]] += 1
for i in range(5):
tot = T.sum()
if tot>0: T /= tot
# Propaga
v = np.array([prob_in.get(i,0) for i in range(5)])
v2 = v @ T
return {i:float(v2) for i in range(5)}

def _pressione(self, stato, run):
media = {0:1.0, 1:1.57, 2:4.24, 3:2.28, 4:1.33}
return min(1.0, run/(media.get(stato,2)*3))

def _run_dist(self, stati, s_cur):
runs, i = [], 0
while i < len(stati):
s=stati; l=1
while i+l<len(stati) and stati[i+l]==s: l+=1
if s==s_cur: runs.append(l)
i+=l
if not runs: return {}
r = np.array(runs)
return dict(media=float(r.mean()), mediana=float(np.median(r)),
p75=float(np.percentile(r,75)), p90=float(np.percentile(r,90)),
mx=int(r.max()), n=len(r))

# ── ritardi tutti i numeri ────────────────────────────────────
def _ritardi_tutti(self, nums, n):
last = {}
for i,arr in enumerate(nums):
for x in arr: last[x] = i
return {x: n-last.get(x,-1)-1 for x in range(1,91)}

# ── selezione numeri ──────────────────────────────────────────
def _seleziona(self, nums, stati, stato_cur):
n = len(nums)
last = {}
for i,arr in enumerate(nums):
for x in arr: last[x] = i
rit = {x: n-last.get(x,-1)-1 for x in range(1,91)}
media_r = np.mean(list(rit.values()))
std_r = np.std(list(rit.values()))

# Frequenza in ultime 50 estrazioni
rec = np.concatenate(nums[max(0,n-50):])
freq_rec = np.bincount(rec.astype(int), minlength=91)[1:].astype(float)

# Frequenza storica in fasi Calde
fc = np.zeros(91)
nc = 0
for i,s in enumerate(stati):
if s==3:
ii = i+WINDOW_ICQ
if ii<len(nums):
for x in nums[ii]: fc[x]+=1
nc+=1
if nc>0: fc/=nc

# Frequenza storica in fasi Turbolente
ft = np.zeros(91)
nt = 0
for i,s in enumerate(stati):
if s==4:
ii = i+WINDOW_ICQ
if ii<len(nums):
for x in nums[ii]: ft[x]+=1
nt+=1
if nt>0: ft/=nt

scores = {}
for num in range(1,91):
r = rit[num]

# A) Ritardo ottimale per stato
if stato_cur in (3,4): # Calda/Turb: premia ritardi bassi-medi
if r <= media_r*0.5: sr = 1.0
elif r <= media_r: sr = 0.85
elif r <= media_r*1.5: sr = 0.50
else: sr = 0.10
else: # Attiva/Neutra: premia ritardi medi-alti
z = (r-media_r)/(std_r+1)
sr = max(0.1, 1.0-abs(z)*0.3)

# B) Fascia numerica per stato
if stato_cur in (3,4):
sf = 0.4
if num%2==0: sf += 0.3 # pari favoriti in Calda
if num<=30 or num>=61: sf += 0.3 # fasce estreme favorite
else:
sf = 0.6
if 31<=num<=60: sf += 0.2 # centro favorito in Attiva

# C) Storico in Calda/Turb
if stato_cur in (3,4):
sc = float(fc[num]) if nc>0 else 0.5
else:
sc = float(freq_rec[num-1])/max(freq_rec.max(),1)

# D) Attività recente
sa = float(freq_rec[num-1])/max(freq_rec.max(),1)

scores[num] = sr*0.35 + sf*0.25 + sc*0.25 + sa*0.15

mx = max(scores.values()) if scores else 1
return sorted(((k,v/mx*100) for k,v in scores.items()), key=lambda x:-x[1])

# ── coppie in fase Calda ──────────────────────────────────────
def _coppie_calda(self, nums, stati):
cp = {}
for i,s in enumerate(stati):
if s==3:
ii = i+WINDOW_ICQ
if ii<len(nums):
arr = sorted(nums[ii])
for a in range(len(arr)):
for b in range(a+1,len(arr)):
k=(arr[a],arr)
cp[k] = cp.get(k,0)+1
return sorted(cp.items(), key=lambda x:-x[1])[:25]

# ── statistiche numeriche per stato ──────────────────────────
def _stats_per_stato(self, nums, stati):
res = {}
for s in range(5):
idxs = [i+WINDOW_ICQ for i,st in enumerate(stati) if st==s and i+WINDOW_ICQ<len(nums)]
if not idxs: continue
all_n = np.concatenate([nums for i in idxs])
res = dict(
n_pari=float(np.mean([np.sum(nums%2==0) for i in idxs])),
media=float(np.mean(all_n)),
n_bassi=float(np.mean([np.sum(nums<=30) for i in idxs])),
n_medi=float(np.mean([np.sum((nums>=31)&(nums<=60)) for i in idxs])),
n_alti=float(np.mean([np.sum(nums>=61) for i in idxs])),
)
return res


# ═══════════════════════════════════════════════════════════════════
# WIDGET HELPER: ScrollFrame
# ═══════════════════════════════════════════════════════════════════
class ScrollFrame(tk.Frame):
def __init__(self, parent, bg=BG, **kw):
super().__init__(parent, bg=bg, **kw)
self.canvas = tk.Canvas(self, bg=bg, highlightthickness=0)
self.vsb = ttk.Scrollbar(self, orient='vertical', command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side='right', fill='y')
self.canvas.pack(side='left', fill='both', expand=True)
self.inner = tk.Frame(self.canvas, bg=bg)
self._id = self.canvas.create_window((0,0), window=self.inner, anchor='nw')
self.inner.bind('<Configure>', self._on_configure)
self.canvas.bind('<Configure>', self._on_canvas_configure)
self.canvas.bind_all('<MouseWheel>', self._on_scroll)

def _on_configure(self, e):
self.canvas.configure(scrollregion=self.canvas.bbox('all'))

def _on_canvas_configure(self, e):
self.canvas.itemconfig(self._id, width=e.width)

def _on_scroll(self, e):
self.canvas.yview_scroll(int(-1*(e.delta/120)), 'units')


# ═══════════════════════════════════════════════════════════════════
# APPLICAZIONE PRINCIPALE
# ═══════════════════════════════════════════════════════════════════
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title('ICQ Predittore · Analisi del Caos e Modello Predittivo Lotto')
self.configure(bg=BG)
self.geometry('1340x900')
self.minsize(1100, 720)
self.motore = MotoreICQ()
self.ris = {} # risultati elaborazione
self.ruota_sel = None
self._build()

# ═══════════════════════════════════════════════════════════════
# COSTRUZIONE UI
# ═══════════════════════════════════════════════════════════════
def _build(self):
self._build_header()
self._build_toolbar()
self._build_body()

# ── header ───────────────────────────────────────────────────
def _build_header(self):
h = tk.Frame(self, bg=ACCENT, height=52)
h.pack(fill='x')
h.pack_propagate(False)
tk.Label(h, text=' ICQ PREDITTORE', font=('Helvetica',16,'bold'),
bg=ACCENT, fg='white').pack(side='left', padx=8, pady=10)
tk.Label(h, text='Analisi del Caos · Modello Predittivo · Lotto Italiano 1939–2026',
font=('Helvetica',9), bg=ACCENT, fg='#93C5FD').pack(side='left', padx=4)
tk.Label(h, text='Alessandro Ginestrino ',
font=('Helvetica',9,'italic'), bg=ACCENT, fg='#93C5FD').pack(side='right')

# ── toolbar ──────────────────────────────────────────────────
def _build_toolbar(self):
tb = tk.Frame(self, bg=BG2, bd=0)
tb.pack(fill='x')
sep(tb, BG4).pack(fill='x')
row = tk.Frame(tb, bg=BG2)
row.pack(fill='x', padx=16, pady=10)

# Pulsante 1: Carica
self.btn_carica = btn(row, '📂 Carica Archivio .txt',
self._carica, ACCENT)
self.btn_carica.pack(side='left', padx=(0,12))

sep_v(row, BG4).pack(side='left', fill='y', padx=8)

# Data fine
tk.Label(row, text='Data fine elaborazione:',
font=FM, bg=BG2, fg=TEXT2).pack(side='left', padx=(0,6))
self.entry_data = tk.Entry(row, font=FM, width=12,
bg='white', fg=TEXT, relief='solid', bd=1,
insertbackground=TEXT)
self.entry_data.insert(0, date.today().strftime('%d/%m/%Y'))
self.entry_data.pack(side='left', padx=(0,10))

# Pulsante 2: Elabora
self.btn_elab = btn(row, '⚙ Elabora', self._elabora, VERDE,
state='disabled')
self.btn_elab.pack(side='left', padx=(0,20))

sep_v(row, BG4).pack(side='left', fill='y', padx=8)

self.lbl_st = tk.Label(row, text='Carica un archivio storico (.txt) per iniziare',
font=FM, bg=BG2, fg=TEXT2)
self.lbl_st.pack(side='left', padx=8)

self.pbar = ttk.Progressbar(row, length=200, mode='determinate')
self.pbar.pack(side='right', padx=8)
sep(tb, BG4).pack(fill='x')

# ── body ─────────────────────────────────────────────────────
def _build_body(self):
body = tk.Frame(self, bg=BG)
body.pack(fill='both', expand=True)

# ── pannello sinistro: classifica ruote ──
self.pnl_sx = tk.Frame(body, bg=BG2, width=320)
self.pnl_sx.pack(side='left', fill='y')
self.pnl_sx.pack_propagate(False)
sep_v(self.pnl_sx, BG4).pack(side='right', fill='y')

tk.Label(self.pnl_sx, text='CLASSIFICA RUOTE', font=FT,
bg=BG2, fg=TEXT).pack(pady=(14,2), padx=14, anchor='w')
tk.Label(self.pnl_sx, text='per Score composito ICQ',
font=FSM, bg=BG2, fg=TEXT3).pack(padx=14, anchor='w')
sep(self.pnl_sx, BG4).pack(fill='x', pady=6)

self.sf_ruote = ScrollFrame(self.pnl_sx, bg=BG2)
self.sf_ruote.pack(fill='both', expand=True)

# ── pannello destro: notebook tab ──
destra = tk.Frame(body, bg=BG)
destra.pack(side='left', fill='both', expand=True)

style = ttk.Style()
style.configure('ICQ.TNotebook', background=BG, borderwidth=0,
tabmargins=[0,0,0,0])
style.configure('ICQ.TNotebook.Tab', font=FB, padding=[14,7],
background=BG3, foreground=TEXT2, borderwidth=0)
style.map('ICQ.TNotebook.Tab',
background=[('selected',BG)],
foreground=[('selected',ACCENT)])

self.nb = ttk.Notebook(destra, style='ICQ.TNotebook')
self.nb.pack(fill='both', expand=True)

# Costruzione 6 tab
self.tabs = {}
tab_defs = [
('panoramica', ' Panoramica '),
('dettaglio', ' Dettaglio Ruota '),
('numeri', ' Selezione Numeri '),
('coppie', ' Coppie Calde '),
('timing', ' Timing Predittivo '),
('riepilogo', ' Riepilogo Operativo '),
]
for key, label in tab_defs:
f = tk.Frame(self.nb, bg=BG)
self.nb.add(f, text=label)
self.tabs[key] = f
# placeholder
tk.Label(f, text='Elabora un archivio per attivare questa sezione.',
font=FM, bg=BG, fg=TEXT3).pack(expand=True)

# ═══════════════════════════════════════════════════════════════
# AZIONI TOOLBAR
# ═══════════════════════════════════════════════════════════════
def _carica(self):
p = filedialog.askopenfilename(
title='Seleziona archivio storico Lotto',
filetypes=[('File testo','*.txt'),('Tutti','*.*')])
if not p: return
try:
dmax = self.motore.carica(p)
self.entry_data.delete(0,'end')
self.entry_data.insert(0, dmax.strftime('%d/%m/%Y'))
self.btn_elab.config(state='normal')
n = len(self.motore.df_raw)
self.lbl_st.config(
text=f'✓ {n:,} righe caricate · ultima data: {dmax}', fg=VERDE)
except Exception as e:
messagebox.showerror('Errore caricamento', str(e))

def _elabora(self):
ds = self.entry_data.get().strip()
try:
d = datetime.strptime(ds, '%d/%m/%Y').strftime('%Y-%m-%d')
except:
messagebox.showerror('Data non valida','Formato atteso: GG/MM/AAAA')
return
self.btn_elab.config(state='disabled')
self.btn_carica.config(state='disabled')
self.pbar['value'] = 0

def worker():
def cb(i, tot, r):
pct = int(i/tot*100)
self.pbar['value'] = pct
self.lbl_st.config(text=f'Elaboro {r}… ({i}/{tot})', fg=TEXT2)
self.update_idletasks()
try:
self.motore.elabora(d, cb)
self.ris = self.motore.risultati
self.after(0, self._post_elabora)
except Exception as e:
self.after(0, lambda: messagebox.showerror('Errore', str(e)))
finally:
self.after(0, lambda: self.btn_elab.config(state='normal'))
self.after(0, lambda: self.btn_carica.config(state='normal'))

threading.Thread(target=worker, daemon=True).start()

def _post_elabora(self):
nr = len(self.ris)
calda = sum(1 for r in self.ris.values() if r['stato']==3)
self.lbl_st.config(
text=f'✓ {nr} ruote elaborate · {calda} in fase CALDA', fg=VERDE)
self.pbar['value'] = 100
self._build_classifica()
self._build_panoramica()
self._build_riepilogo()

# ═══════════════════════════════════════════════════════════════
# CLASSIFICA RUOTE (pannello sinistro)
# ═══════════════════════════════════════════════════════════════
def _build_classifica(self):
for w in self.sf_ruote.inner.winfo_children():
w.destroy()

ordinate = sorted(self.ris.items(), key=lambda x:-x[1]['score'])

for rank, (ruota, r) in enumerate(ordinate):
c = COL_ST[r['stato']]
em = EMO_ST[r['stato']]
bg_card = '#FFF7F4' if r['stato']==3 else \
'#F0FAFF' if r['stato']==4 else \
'#F6F8FF' if r['stato']==2 else BG2

card = tk.Frame(self.sf_ruote.inner, bg=bg_card,
highlightbackground=BG4, highlightthickness=1,
cursor='hand2')
card.pack(fill='x', padx=6, pady=3)

# Striscia colorata sinistra
tk.Frame(card, bg=c, width=5).pack(side='left', fill='y')

inner = tk.Frame(card, bg=bg_card)
inner.pack(side='left', fill='both', expand=True, padx=10, pady=8)

# Riga 1: rank, nome, score
r1 = tk.Frame(inner, bg=bg_card)
r1.pack(fill='x')
tk.Label(r1, text=f'{rank+1:2d}.', font=FSM, bg=bg_card,
fg=TEXT3, width=3).pack(side='left')
tk.Label(r1, text=f'{ruota} — {r["nome"]}', font=FB,
bg=bg_card, fg=TEXT).pack(side='left')
tk.Label(r1, text=f'{r["score"]:.1f}', font=('Helvetica',11,'bold'),
bg=bg_card, fg=ACCENT).pack(side='right')

# Riga 2: stato, ICQ, run, trend
r2 = tk.Frame(inner, bg=bg_card)
r2.pack(fill='x', pady=(3,0))
tk.Label(r2, text=f'{em} {r["stato_nome"]}', font=FSB,
bg=bg_card, fg=c).pack(side='left')
tk.Label(r2, text=f' ICQ {r["icq"]:.1f}', font=FSM,
bg=bg_card, fg=TEXT2).pack(side='left')
tk.Label(r2, text=f' Run {r["run"]}', font=FSM,
bg=bg_card, fg=TEXT2).pack(side='left')
ts = '↑' if r['trend']>0.3 else ('↓' if r['trend']<-0.3 else '→')
tc = VERDE if r['trend']>0.3 else (ROSSO if r['trend']<-0.3 else TEXT3)
tk.Label(r2, text=f' {ts}{r["trend"]:+.1f}', font=FSM,
bg=bg_card, fg=tc).pack(side='left')

# Barra ICQ
bar_bg = tk.Frame(inner, bg=BG4, height=4)
bar_bg.pack(fill='x', pady=(5,0))
w_bar = max(4, int(r['icq']/100*280))
tk.Frame(bar_bg, bg=c, width=w_bar, height=4).place(x=0,y=0)

# Binding click
for widget in [card, inner, r1, r2, bar_bg]:
widget.bind('<Button-1>', lambda e,rv=ruota: self._sel_ruota(rv))
for child in widget.winfo_children():
child.bind('<Button-1>', lambda e,rv=ruota: self._sel_ruota(rv))

# ═══════════════════════════════════════════════════════════════
# TAB PANORAMICA
# ═══════════════════════════════════════════════════════════════
def _build_panoramica(self):
f = self.tabs['panoramica']
clear(f)

sf = ScrollFrame(f)
sf.pack(fill='both', expand=True)
inn = sf.inner

tk.Label(inn, text='PANORAMICA ICQ — TUTTE LE RUOTE', font=FH,
bg=BG, fg=TEXT).pack(pady=(18,4), padx=20, anchor='w')
tk.Label(inn,
text='Ogni barra rappresenta il valore ICQ corrente. '
'La linea arancione marca la soglia CALDA (ICQ=70). '
'Clicca una ruota nella classifica per il dettaglio.',
font=FSM, bg=BG, fg=TEXT3).pack(padx=20, anchor='w')
sep(inn, BG3).pack(fill='x', padx=20, pady=10)

ordinate = sorted(self.ris.items(), key=lambda x:-x[1]['score'])
BAR_MAX = 500

for ruota, r in ordinate:
c = COL_ST[r['stato']]
row = tk.Frame(inn, bg=BG)
row.pack(fill='x', padx=20, pady=4)

# Nome ruota
tk.Label(row, text=f'{ruota}', font=FB, bg=BG, fg=TEXT,
width=4, anchor='w').pack(side='left')
tk.Label(row, text=r['nome'], font=FSM, bg=BG, fg=TEXT2,
width=10, anchor='w').pack(side='left')

# Barra con soglie
bar_outer = tk.Canvas(row, bg=BG3, height=24,
width=BAR_MAX, highlightthickness=0)
bar_outer.pack(side='left', padx=6)
# Zona calda (70–85)
x70 = int(0.70*BAR_MAX); x85 = int(0.85*BAR_MAX)
bar_outer.create_rectangle(x70,0,x85,24, fill='#FEF3C7', outline='')
# Barra valore
xv = max(4, int(r['icq']/100*BAR_MAX))
bar_outer.create_rectangle(0,2,xv,22, fill=c, outline='')
# Linea soglia 70
bar_outer.create_line(x70,0,x70,24, fill=ARANCIO, width=1)
bar_outer.create_line(x85,0,x85,24, fill=ROSSO, width=1, dash=(3,3))

# Valori numerici
tk.Label(row, text=f'ICQ {r["icq"]:5.1f}', font=FMO,
bg=BG, fg=TEXT, width=10).pack(side='left', padx=4)
tk.Label(row, text=f'Score {r["score"]:5.1f}', font=FMO,
bg=BG, fg=ACCENT, width=12).pack(side='left')

# Stato + trend
em = EMO_ST[r['stato']]
tk.Label(row, text=f'{em} {r["stato_nome"]:12s}', font=FSB,
bg=BG, fg=c, width=16).pack(side='left')
ts = '↑' if r['trend']>0.3 else ('↓' if r['trend']<-0.3 else '→')
tc = VERDE if r['trend']>0.3 else (ROSSO if r['trend']<-0.3 else TEXT3)
tk.Label(row, text=f'{ts}{r["trend"]:+.2f}', font=FSM,
bg=BG, fg=tc, width=7).pack(side='left')
tk.Label(row, text=f'Run {r["run"]:2d}', font=FSM,
bg=BG, fg=TEXT2, width=7).pack(side='left')

sep(inn, BG3).pack(fill='x', padx=20, pady=16)

# Legenda stati
tk.Label(inn, text='LEGENDA STATI ICQ', font=FT, bg=BG, fg=TEXT).pack(padx=20, anchor='w')
leg = tk.Frame(inn, bg=BG)
leg.pack(padx=20, pady=8, anchor='w')

defs = [
(0,'Fredda','ICQ 0–20 — Alta dispersione, bassa aggregazione'),
(1,'Neutra','ICQ 20–40 — Clima nella media, nessun segnale'),
(2,'Attiva','ICQ 40–70 — Aggregazione moderata, fase ordinaria'),
(3,'Calda','ICQ 70–85 — ★ FASE OTTIMALE — Alta aggregazione, inerzia elevata'),
(4,'Turbolenta','ICQ 85–100 — Aggregazione estrema, alta instabilità'),
]
for s,nome,desc in defs:
lr = tk.Frame(leg, bg=BG)
lr.pack(fill='x', pady=3)
tk.Frame(lr, bg=COL_ST, width=14, height=14).pack(side='left', padx=(0,8))
tk.Label(lr, text=f'{nome:14s}', font=FB, bg=BG,
fg=COL_ST, width=12, anchor='w').pack(side='left')
tk.Label(lr, text=desc, font=FSM, bg=BG, fg=TEXT2).pack(side='left')

# ═══════════════════════════════════════════════════════════════
# SELEZIONE RUOTA
# ═══════════════════════════════════════════════════════════════
def _sel_ruota(self, ruota):
self.ruota_sel = ruota
r = self.ris[ruota]
self._build_dettaglio(r)
self._build_numeri(r)
self._build_coppie(r)
self._build_timing(r)
self.nb.select(1)

# ═══════════════════════════════════════════════════════════════
# TAB DETTAGLIO RUOTA
# ═══════════════════════════════════════════════════════════════
def _build_dettaglio(self, r):
f = self.tabs['dettaglio']
clear(f)

c = COL_ST[r['stato']]
em = EMO_ST[r['stato']]
bg2 = '#FFF7F4' if r['stato']==3 else BG2

# Intestazione colorata
hdr = tk.Frame(f, bg=c, height=5)
hdr.pack(fill='x')

sf = ScrollFrame(f)
sf.pack(fill='both', expand=True)
inn = sf.inner

# Titolo
trow = tk.Frame(inn, bg=bg2)
trow.pack(fill='x', padx=20, pady=(16,8))
tk.Label(trow, text=f'{em} {r["ruota"]} — {r["nome"]}',
font=FH2, bg=bg2, fg=TEXT).pack(anchor='w')
tk.Label(trow, text=f'Elaborazione su {r["n_est"]:,} estrazioni · '
f'{r["n_calda"]} concorsi in Calda · {r["n_turb"]} in Turbolenta',
font=FSM, bg=bg2, fg=TEXT3).pack(anchor='w', pady=(2,0))

sep(inn, BG4).pack(fill='x', padx=20)

# Metriche principali in 3 colonne
met = tk.Frame(inn, bg=bg2)
met.pack(fill='x', padx=20, pady=14)

metriche = [
('Stato ICQ', f'{em} {r["stato_nome"]}', c),
('ICQ corrente', f'{r["icq"]:.1f} / 100', ACCENT),
('Score ruota', f'{r["score"]:.1f} / 100', ACCENT),
('Run attuale', f'{r["run"]} concorsi', TEXT),
('Trend ICQ', f'{"↑" if r["trend"]>0.3 else "↓" if r["trend"]<-0.3 else "→"} {r["trend"]:+.2f}',
VERDE if r["trend"]>0.3 else ROSSO if r["trend"]<-0.3 else TEXT2),
('Pressione inv.', f'{r["pressione"]*100:.0f}%',
ROSSO if r["pressione"]>0.6 else ARANCIO if r["pressione"]>0.35 else VERDE),
]
for i,(lbl,val,vc) in enumerate(metriche):
col = i%3
row_i = i//3
cell = tk.Frame(met, bg=BG3, highlightbackground=BG4, highlightthickness=1)
cell.grid(row=row_i, column=col, padx=5, pady=5, sticky='nsew')
met.columnconfigure(col, weight=1)
tk.Label(cell, text=lbl, font=FSM, bg=BG3, fg=TEXT3).pack(anchor='w', padx=12, pady=(10,2))
tk.Label(cell, text=val, font=('Helvetica',13,'bold'), bg=BG3, fg=vc).pack(anchor='w', padx=12, pady=(0,10))

sep(inn, BG4).pack(fill='x', padx=20, pady=4)

# Probabilità transizione
tk.Label(inn, text='Probabilità stato al prossimo concorso',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(12,8))

prow = tk.Frame(inn, bg=BG)
prow.pack(fill='x', padx=20)
BAR = 300
for s in range(5):
p = r['prob'].get(s,0)
if p < 0.005: continue
pr = tk.Frame(prow, bg=BG)
pr.pack(fill='x', pady=4)
tk.Label(pr, text=f'{EMO_ST} {NOM_ST}', font=FB,
bg=BG, fg=COL_ST, width=16, anchor='w').pack(side='left')
tk.Label(pr, text=f'{p*100:5.1f}%', font=FMO,
bg=BG, fg=TEXT, width=7, anchor='e').pack(side='left', padx=6)
bb = tk.Frame(pr, bg=BG3, height=18, width=BAR)
bb.pack(side='left')
bb.pack_propagate(False)
tk.Frame(bb, bg=COL_ST, width=max(2,int(p*BAR)), height=18).place(x=0,y=0)

sep(inn, BG4).pack(fill='x', padx=20, pady=12)

# Run distribution
rd = r.get('run_dist',{})
if rd:
tk.Label(inn, text=f'Distribuzione storica run "{r["stato_nome"]}" su questa ruota',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(0,8))
rdframe = tk.Frame(inn, bg=BG)
rdframe.pack(fill='x', padx=20)

stat_rows = [
('Durata media', f'{rd["media"]:.1f} concorsi'),
('Mediana', f'{rd["mediana"]:.0f} concorsi'),
('75° percentile', f'{rd["p75"]:.1f} concorsi'),
('90° percentile', f'{rd["p90"]:.1f} concorsi'),
('Massimo storico', f'{rd["mx"]} concorsi'),
('Episodi totali', f'{rd["n"]}'),
('Run corrente', f'{r["run"]} concorsi ← sei qui'),
]
for lbl,val in stat_rows:
sr = tk.Frame(rdframe, bg=BG2,
highlightbackground=BG4, highlightthickness=1)
sr.pack(fill='x', pady=2)
tk.Label(sr, text=lbl, font=FM, bg=BG2, fg=TEXT2,
width=22, anchor='w').pack(side='left', padx=10, pady=5)
vc = ROSSO if 'corrente' in lbl and r['run']>rd.get('p75',99) \
else ARANCIO if 'corrente' in lbl and r['run']>rd.get('media',99) \
else ACCENT
tk.Label(sr, text=val, font=FB, bg=BG2, fg=vc).pack(side='right', padx=10)

sep(inn, BG4).pack(fill='x', padx=20, pady=12)

# Statistiche numeriche per stato
ss = r.get('stats_stato',{})
if ss:
tk.Label(inn, text='Caratteristiche numeriche medie per stato ICQ (questa ruota)',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(0,8))

tbl = tk.Frame(inn, bg=BG)
tbl.pack(padx=20, anchor='w')
hdrs = ['Stato','N. Pari / 5','Media num','N. Bassi','N. Medi','N. Alti']
ws = [12, 12, 10, 10, 10, 10]
for j,(h,w) in enumerate(zip(hdrs,ws)):
tk.Label(tbl, text=h, font=FSB, bg=BG3, fg=TEXT2,
width=w, anchor='center', pady=5).grid(
row=0, column=j, padx=1, pady=1, sticky='nsew')
for i,s in enumerate(range(5)):
if s not in ss: continue
st = ss
bg_r = BG2 if i%2==0 else BG
vals = [NOM_ST,
f'{st["n_pari"]:.2f}',
f'{st["media"]:.1f}',
f'{st["n_bassi"]:.2f}',
f'{st["n_medi"]:.2f}',
f'{st["n_alti"]:.2f}']
for j,(v,w) in enumerate(zip(vals,ws)):
fg = COL_ST if j==0 else TEXT
fnt = FSB if j==0 else FMO
tk.Label(tbl, text=v, font=fnt, bg=bg_r, fg=fg,
width=w, anchor='center', pady=4).grid(
row=i+1, column=j, padx=1, pady=1, sticky='nsew')

sep(inn, BG4).pack(fill='x', padx=20, pady=12)

# Cronologia ICQ ultimi 20 concorsi
tk.Label(inn, text='Cronologia ICQ — ultimi 20 concorsi',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(0,8))

cron_frame = tk.Frame(inn, bg=BG)
cron_frame.pack(padx=20, anchor='w')
hdrs2 = ['Data','ICQ','Stato']
ws2 = [10, 8, 14]
for j,(h,w) in enumerate(zip(hdrs2,ws2)):
tk.Label(cron_frame, text=h, font=FSB, bg=BG3, fg=TEXT2,
width=w, anchor='center', pady=5).grid(
row=0, column=j, padx=1, pady=1, sticky='nsew')

for i,(dt,iq,st) in enumerate(r['cronologia']):
bg_r = '#FFF7F4' if st==3 else BG2 if i%2==0 else BG
tk.Label(cron_frame, text=dt, font=FMO, bg=bg_r, fg=TEXT2,
width=10, anchor='center', pady=3).grid(
row=i+1, column=0, padx=1, pady=1, sticky='nsew')
tk.Label(cron_frame, text=f'{iq:.1f}', font=FMO, bg=bg_r,
fg=COL_ST[st], width=8, anchor='center').grid(
row=i+1, column=1, padx=1, pady=1, sticky='nsew')
tk.Label(cron_frame, text=f'{EMO_ST[st]} {NOM_ST[st]}', font=FSB,
bg=bg_r, fg=COL_ST[st], width=14, anchor='center').grid(
row=i+1, column=2, padx=1, pady=1, sticky='nsew')

# ═══════════════════════════════════════════════════════════════
# TAB SELEZIONE NUMERI
# ═══════════════════════════════════════════════════════════════
def _build_numeri(self, r):
f = self.tabs['numeri']
clear(f)

c = COL_ST[r['stato']]
tk.Frame(f, bg=c, height=5).pack(fill='x')

sf = ScrollFrame(f)
sf.pack(fill='both', expand=True)
inn = sf.inner

tk.Label(inn, text=f'Selezione Numeri · {r["ruota"]} — {r["nome"]} ({r["stato_nome"]})',
font=FH, bg=BG, fg=TEXT).pack(pady=(16,4), padx=20, anchor='w')
tk.Label(inn,
text='Score composito: ritardo ottimale (35%) + fascia ICQ (25%) + '
'storico fasi Calde (25%) + attività recente (15%)',
font=FSM, bg=BG, fg=TEXT3).pack(padx=20, anchor='w')
sep(inn, BG3).pack(fill='x', padx=20, pady=10)

top20 = r['numeri'][:20]

# ── Griglia 5×4 con cerchi numeri ──
tk.Label(inn, text='TOP 20 — Visualizzazione grafica', font=FT,
bg=BG, fg=TEXT).pack(padx=20, anchor='w', pady=(0,8))

grid = tk.Frame(inn, bg=BG)
grid.pack(padx=20, anchor='w')

for i,(num,sc) in enumerate(top20):
ri, ci = divmod(i, 5)
cell = tk.Frame(grid, bg=BG)
cell.grid(row=ri, column=ci, padx=6, pady=6)

# Cerchio colorato
intens = sc/100
if r['stato']==3:
# Calda: gradiente arancione
r_c = int(220 + 35*(1-intens))
g_c = int(80 + 80*(1-intens))
b_c = int(30 + 50*(1-intens))
elif r['stato']==4:
r_c = int(200); g_c = int(30); b_c = int(30)
else:
r_c = int(30); g_c = int(120); b_c = int(220)
hex_c = f'#{min(255,r_c):02x}{min(255,g_c):02x}{min(255,b_c):02x}'

cv = tk.Canvas(cell, width=64, height=64, bg=BG,
highlightthickness=0)
cv.pack()
cv.create_oval(4,4,60,60, fill=hex_c, outline='')
cv.create_text(32,28, text=str(num),
font=('Helvetica',16,'bold'), fill='white')
cv.create_text(32,46, text=f'{sc:.0f}',
font=('Helvetica',9), fill='white')

# Fascia e parità
fascia = '1–30' if num<=30 else ('31–60' if num<=60 else '61–90')
par = 'P' if num%2==0 else 'D'
tk.Label(cell, text=f'{fascia} {par}', font=FSM,
bg=BG, fg=TEXT3).pack()

sep(inn, BG3).pack(fill='x', padx=20, pady=14)

# ── Tabella completa top 45 con ritardi ──
tk.Label(inn, text='Tabella completa — Top 45 numeri con ritardi',
font=FT, bg=BG, fg=TEXT).pack(padx=20, anchor='w', pady=(0,8))

tbl = tk.Frame(inn, bg=BG)
tbl.pack(padx=20, anchor='w')

hdrs = ['#','Num','Score','Ritardo','Fascia','Par','Ruolo']
ws = [3, 5, 7, 8, 8, 5, 16]
for j,(h,w) in enumerate(zip(hdrs,ws)):
tk.Label(tbl, text=h, font=FSB, bg=BG3, fg=TEXT2,
width=w, anchor='center', pady=5).grid(
row=0, column=j, padx=1, pady=1, sticky='nsew')

ritardi = r['ritardi']
rit_vals = list(ritardi.values())
media_r = np.mean(rit_vals)

for i,(num,sc) in enumerate(r['numeri'][:45]):
bg_r = '#FFF7F4' if r['stato']==3 and i<10 else BG2 if i%2==0 else BG
rit = ritardi.get(num,0)
fascia = '1–30' if num<=30 else ('31–60' if num<=60 else '61–90')
par = 'Pari' if num%2==0 else 'Disp.'

# Ruolo semantico
if rit <= media_r*0.5: ruolo = '🔥 Caldo'
elif rit <= media_r: ruolo = '◎ Tiepido'
elif rit <= media_r*1.8: ruolo = '→ Medio'
else: ruolo = '❄ Ritardante'

rit_col = VERDE if rit<=media_r*0.5 else \
ARANCIO if rit<=media_r else \
TEXT2 if rit<=media_r*1.8 else ROSSO

row_vals = [i+1, num, f'{sc:.1f}', rit, fascia, par[:1], ruolo]
fgs = [TEXT3, ACCENT, TEXT, rit_col, TEXT2, TEXT2, TEXT2]
fnts = [FSM, FB, FMO, FMO, FSM, FSM, FSM]
for j,(v,w,fg,fn) in enumerate(zip(row_vals,ws,fgs,fnts)):
tk.Label(tbl, text=str(v), font=fn, bg=bg_r, fg=fg,
width=w, anchor='center', pady=3).grid(
row=i+1, column=j, padx=1, pady=1, sticky='nsew')

sep(inn, BG3).pack(fill='x', padx=20, pady=14)

# ── Distribuzione ritardi tutti i 90 numeri ──
tk.Label(inn, text='Distribuzione ritardi — tutti i 90 numeri',
font=FT, bg=BG, fg=TEXT).pack(padx=20, anchor='w', pady=(0,6))
tk.Label(inn, text='Ogni riga = un numero. Barra = ritardo relativo alla media.',
font=FSM, bg=BG, fg=TEXT3).pack(padx=20, anchor='w')

canvas_rit = tk.Canvas(inn, bg=BG, height=240, highlightthickness=0)
canvas_rit.pack(fill='x', padx=20, pady=8)
self.after(50, lambda cv=canvas_rit, rv=r: self._draw_ritardi(cv, rv))

def _draw_ritardi(self, canvas, r):
canvas.update_idletasks()
W = canvas.winfo_width(); H = canvas.winfo_height()
if W < 10: W = 900

ritardi = r['ritardi']
rit_vals = [ritardi[x] for x in range(1,91)]
max_r = max(rit_vals)
media_r = np.mean(rit_vals)

rows = 9; cols = 10
cell_w = W/cols; cell_h = H/rows

for i,num in enumerate(range(1,91)):
ri,ci = divmod(i,cols)
x0 = ci*cell_w; y0 = ri*cell_h
rit = ritardi[num]
ratio = rit/max_r
# Colore: verde=basso ritardo, arancio=medio, rosso=alto
if rit <= media_r*0.5: col='#059669'
elif rit <= media_r: col='#D97706'
elif rit <= media_r*1.8: col='#0284C7'
else: col='#DC2626'
# Sfondo cella
canvas.create_rectangle(x0+1,y0+1,x0+cell_w-1,y0+cell_h-1,
fill='#F9FAFB',outline='#E5E7EB')
# Barra ritardo
bw = int(ratio*(cell_w-4))
canvas.create_rectangle(x0+2,y0+cell_h*0.6,
x0+2+bw,y0+cell_h-2,
fill=col,outline='')
# Numero
canvas.create_text(x0+cell_w/2, y0+cell_h*0.3,
text=str(num),
font=('Helvetica',8,'bold'), fill='#111827')
# Ritardo
canvas.create_text(x0+cell_w/2, y0+cell_h*0.75,
text=str(rit),
font=('Helvetica',7), fill=col)

# ═══════════════════════════════════════════════════════════════
# TAB COPPIE CALDE
# ═══════════════════════════════════════════════════════════════
def _build_coppie(self, r):
f = self.tabs['coppie']
clear(f)

c = COL_ST[r['stato']]
tk.Frame(f, bg=c, height=5).pack(fill='x')

sf = ScrollFrame(f)
sf.pack(fill='both', expand=True)
inn = sf.inner

tk.Label(inn, text=f'Coppie in Fase Calda · {r["ruota"]} — {r["nome"]}',
font=FH, bg=BG, fg=TEXT).pack(pady=(16,4), padx=20, anchor='w')
tk.Label(inn,
text='Coppie di numeri che sono uscite insieme più frequentemente '
'durante le fasi Calde di questa ruota. '
'Dato puramente storico — non garantisce ripetizione.',
font=FSM, bg=BG, fg=TEXT3, wraplength=700,
justify='left').pack(padx=20, anchor='w')
sep(inn, BG3).pack(fill='x', padx=20, pady=10)

coppie = r.get('coppie',[])
if not coppie:
tk.Label(inn, text='Dati insufficienti (troppo poche fasi Calde).',
font=FM, bg=BG, fg=TEXT3).pack(padx=20)
return

max_f = coppie[0][1]

tk.Label(inn, text='TOP 25 COPPIE — co-occorrenze in fasi Calde',
font=FT, bg=BG, fg=TEXT).pack(padx=20, anchor='w', pady=(0,8))

tbl = tk.Frame(inn, bg=BG)
tbl.pack(padx=20, anchor='w')

hdrs = ['#','Num A','Num B','Frequenza','Intensità (%)','Forza']
ws = [3, 7, 7, 11, 13, 22]
for j,(h,w) in enumerate(zip(hdrs,ws)):
tk.Label(tbl, text=h, font=FSB, bg=BG3, fg=TEXT2,
width=w, anchor='center', pady=5).grid(
row=0, column=j, padx=1, pady=1, sticky='nsew')

for i,((a,b),freq) in enumerate(coppie[:25]):
bg_r = BG2 if i%2==0 else BG
forza = freq/max_f
pct = f'{forza*100:.1f}%'
bar = '█'*int(forza*18) + '░'*(18-int(forza*18))
bc = COL_ST[3] if forza>0.6 else ARANCIO if forza>0.3 else TEXT3
row_v = [i+1, a, b, freq, pct, bar]
fgs = [TEXT3, ACCENT, ACCENT, TEXT, TEXT2, bc]
fnts = [FSM, FB, FB, FMO, FMO, ('Courier',9)]
for j,(v,w,fg,fn) in enumerate(zip(row_v,ws,fgs,fnts)):
tk.Label(tbl, text=str(v), font=fn, bg=bg_r, fg=fg,
width=w, anchor='center' if j!=5 else 'w',
pady=4).grid(row=i+1, column=j, padx=1, pady=1, sticky='nsew')

sep(inn, BG3).pack(fill='x', padx=20, pady=14)

# Matrice di co-occorrenza visuale (heatmap su canvas)
tk.Label(inn, text='Mini-mappa co-occorrenze (top 15 coppie — intensità colore)',
font=FT, bg=BG, fg=TEXT).pack(padx=20, anchor='w', pady=(0,6))

cv_heat = tk.Canvas(inn, bg=BG, height=200, highlightthickness=0)
cv_heat.pack(fill='x', padx=20, pady=8)
self.after(50, lambda cv=cv_heat, cp=coppie[:15], mf=max_f: self._draw_heatmap(cv, cp, mf))

def _draw_heatmap(self, canvas, coppie, max_f):
canvas.update_idletasks()
W = canvas.winfo_width(); H = canvas.winfo_height()
if W<10: W=900
n = len(coppie)
if n==0: return

# Disposizione: rettangoli proporzionali alla frequenza
step = W/n
for i,((a,b),freq) in enumerate(coppie):
x0 = i*step+2; x1 = (i+1)*step-2
ratio = freq/max_f
h = int(ratio*(H-60))
y0 = H-30-h; y1 = H-30
r_c = int(234*ratio+20*(1-ratio))
g_c = int(90*(1-ratio)+150*ratio)
b_c = int(26*(1-ratio))
col = f'#{min(255,r_c):02x}{min(255,g_c):02x}{min(255,b_c):02x}'
canvas.create_rectangle(x0,y0,x1,y1, fill=col, outline='')
canvas.create_text((x0+x1)/2, y1+14,
text=f'{a}–{b}', font=('Helvetica',8), fill=TEXT2)
canvas.create_text((x0+x1)/2, y0-10,
text=str(freq), font=('Helvetica',8,'bold'), fill=TEXT)

# ═══════════════════════════════════════════════════════════════
# TAB TIMING PREDITTIVO
# ═══════════════════════════════════════════════════════════════
def _build_timing(self, r):
f = self.tabs['timing']
clear(f)

c = COL_ST[r['stato']]
tk.Frame(f, bg=c, height=5).pack(fill='x')

sf = ScrollFrame(f)
sf.pack(fill='both', expand=True)
inn = sf.inner

tk.Label(inn, text=f'Timing Predittivo · {r["ruota"]} — {r["nome"]}',
font=FH, bg=BG, fg=TEXT).pack(pady=(16,4), padx=20, anchor='w')
sep(inn, BG3).pack(fill='x', padx=20, pady=8)

# ── Valutazione momento ──
sm, colm, labm, descm = self._valuta_momento(r)

mom_frame = tk.Frame(inn, bg='#F0FDF4' if sm>=70 else '#FFFBEB' if sm>=45 else BG2,
highlightbackground=colm, highlightthickness=2)
mom_frame.pack(fill='x', padx=20, pady=4)
mom_inn = tk.Frame(mom_frame, bg=mom_frame['bg'])
mom_inn.pack(padx=16, pady=14, fill='x')

tk.Label(mom_inn, text=f'◆ {labm}',
font=('Helvetica',15,'bold'), bg=mom_frame['bg'], fg=colm).pack(anchor='w')
tk.Label(mom_inn, text=descm,
font=FM, bg=mom_frame['bg'], fg=TEXT2,
wraplength=680, justify='left').pack(anchor='w', pady=(4,6))

# Score momento barra
brow = tk.Frame(mom_inn, bg=mom_frame['bg'])
brow.pack(fill='x')
tk.Label(brow, text=f'Score momento: {sm:.0f}/100',
font=FB, bg=mom_frame['bg'], fg=ACCENT).pack(side='left')
bb = tk.Frame(brow, bg=BG3, height=8, width=300)
bb.pack(side='left', padx=12)
bb.pack_propagate(False)
tk.Frame(bb, bg=colm, width=int(sm*3), height=8).place(x=0,y=0)

sep(inn, BG3).pack(fill='x', padx=20, pady=12)

# ── Proiezione t+1, t+2, t+3 ──
tk.Label(inn, text='Proiezione probabilistica — prossimi 3 concorsi',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(0,10))

proj = tk.Frame(inn, bg=BG)
proj.pack(fill='x', padx=20)

for step,(label,prob_d) in enumerate([
('t+1 — Prossimo concorso', r['prob']),
('t+2 — Tra 2 concorsi', r['prob2']),
('t+3 — Tra 3 concorsi', r['prob3']),
]):
col_f = tk.Frame(proj, bg=BG2,
highlightbackground=BG4, highlightthickness=1)
col_f.pack(side='left', fill='both', expand=True, padx=(0,8))

tk.Label(col_f, text=label, font=FSB, bg=c, fg='white',
pady=7).pack(fill='x')

best = max(prob_d, key=prob_d.get)
for s in range(5):
p = prob_d.get(s,0)
if p < 0.005: continue
pr = tk.Frame(col_f, bg=BG2)
pr.pack(fill='x', padx=10, pady=3)
mk = ' ◄' if s==best else ''
tk.Label(pr, text=f'{EMO_ST} {NOM_ST}{mk}',
font=FSB if s==best else FSM,
bg=BG2, fg=COL_ST, anchor='w').pack(side='left')
tk.Label(pr, text=f'{p*100:.1f}%', font=FMO,
bg=BG2, fg=TEXT).pack(side='right')

tk.Frame(col_f, bg=BG, height=8).pack()

sep(inn, BG3).pack(fill='x', padx=20, pady=12)

# ── Grafico andamento ICQ ──
tk.Label(inn, text='Andamento ICQ — ultimi 40 concorsi',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(0,6))

cv = tk.Canvas(inn, bg='#FAFBFC', height=180,
highlightbackground=BG4, highlightthickness=1)
cv.pack(fill='x', padx=20, pady=4)
icq_plot = r['icq_arr'][-40:] if len(r['icq_arr'])>=40 else r['icq_arr']
self.after(50, lambda canvas=cv, arr=icq_plot, st=r['stato']: \
self._draw_icq(canvas, arr, st))

sep(inn, BG3).pack(fill='x', padx=20, pady=12)

# ── Guida interpretativa ──
tk.Label(inn, text='Come interpretare il timing ICQ',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(0,8))

guide = [
(VERDE, 'MOMENTO OTTIMALE (score 75–100)',
'Ingresso in Calda recente (run ≤ 2), ICQ in salita, pressione bassa. '
'Massima probabilità di continuazione. Fase ideale per applicare la selezione numeri.'),
(ARANCIO, 'MOMENTO FAVOREVOLE (score 50–74)',
'Calda in corso con run ≤ 5. La pressione cresce ma la fase continua '
'con buona probabilità. Monitorare il trend ICQ.'),
(ROSSO, 'MOMENTO A RISCHIO (score 30–49)',
'Calda prolungata (run > 5) o Turbolenta instabile. '
'La run supera la media storica, alta probabilità di inversione imminente.'),
(ACC2, 'INGRESSO CALDA PROBABILE',
'Stato Attiva con trend fortemente positivo. '
'Il sistema si avvicina alla soglia 70. Prepararsi all\'ingresso.'),
(TEXT2, 'FASE NON FAVOREVOLE',
'Stato Fredda, Neutra, o Attiva senza segnale. '
'Attendere il prossimo ingresso in Calda con ICQ > 70.'),
]
for gc,gl,gd in guide:
gr = tk.Frame(inn, bg=BG2, highlightbackground=BG4, highlightthickness=1)
gr.pack(fill='x', padx=20, pady=3)
tk.Frame(gr, bg=gc, width=5).pack(side='left', fill='y')
gi = tk.Frame(gr, bg=BG2)
gi.pack(side='left', padx=12, pady=8, fill='both', expand=True)
tk.Label(gi, text=gl, font=FB, bg=BG2, fg=gc).pack(anchor='w')
tk.Label(gi, text=gd, font=FSM, bg=BG2, fg=TEXT2,
wraplength=620, justify='left').pack(anchor='w', pady=(2,0))

def _draw_icq(self, canvas, arr, stato_cur):
canvas.update_idletasks()
W = canvas.winfo_width(); H = canvas.winfo_height()
if W<10: W=900
if len(arr)<2: return

pad_l=40; pad_r=12; pad_t=12; pad_b=24
gw=W-pad_l-pad_r; gh=H-pad_t-pad_b

def px(i): return pad_l + int(i/(len(arr)-1)*gw)
def py(v): return pad_t + int((1-v/100)*gh)

# Zona Calda
canvas.create_rectangle(pad_l, py(85), W-pad_r, py(70),
fill='#FEF3C7', outline='')
canvas.create_rectangle(pad_l, py(70), W-pad_r, py(85),
fill='#FEF3C7', outline='')

# Griglia orizzontale
for v in [20,40,70,85,100]:
y_ = py(v)
canvas.create_line(pad_l, y_, W-pad_r, y_,
fill='#E5E7EB', width=1)
canvas.create_text(pad_l-5, y_, text=str(v),
font=('Helvetica',7), fill=TEXT3, anchor='e')

# Linea ICQ
pts = [(px(i), py(v)) for i,v in enumerate(arr)]
for i in range(len(pts)-1):
v = arr[i+1]
if v >= 85: col = COL_ST[4]
elif v >= 70: col = COL_ST[3]
elif v >= 40: col = COL_ST[2]
elif v >= 20: col = COL_ST[1]
else: col = COL_ST[0]
canvas.create_line(pts[0], pts[1],
pts[i+1][0], pts[i+1][1],
fill=col, width=2, smooth=True)

# Punto finale
px_, py_ = pts[-1]
canvas.create_oval(px_-5,py_-5,px_+5,py_+5,
fill=COL_ST[stato_cur], outline='white', width=2)

# Etichette soglie
canvas.create_text(W-pad_r-2, py(77), text='CALDA',
font=('Helvetica',8,'bold'), fill=COL_ST[3], anchor='e')
canvas.create_text(W-pad_r-2, py(91), text='TURB.',
font=('Helvetica',8), fill=COL_ST[4], anchor='e')

# ═══════════════════════════════════════════════════════════════
# TAB RIEPILOGO OPERATIVO
# ═══════════════════════════════════════════════════════════════
def _build_riepilogo(self):
f = self.tabs['riepilogo']
clear(f)

sf = ScrollFrame(f)
sf.pack(fill='both', expand=True)
inn = sf.inner

tk.Label(inn, text='RIEPILOGO OPERATIVO — TUTTE LE RUOTE',
font=FH, bg=BG, fg=TEXT).pack(pady=(18,4), padx=20, anchor='w')
tk.Label(inn,
text='Sintesi dei segnali d\'azione per ogni ruota, ordinata per priorità operativa.',
font=FSM, bg=BG, fg=TEXT3).pack(padx=20, anchor='w')
sep(inn, BG3).pack(fill='x', padx=20, pady=10)

# Ordina per score momento
dati = []
for ruota, r in self.ris.items():
sm, colm, labm, descm = self._valuta_momento(r)
dati.append((ruota, r, sm, colm, labm))

dati.sort(key=lambda x:-x[2])

# ── Sezione 1: Segnali OTTIMALI ──
ottimali = [d for d in dati if d[2]>=70]
favorevoli = [d for d in dati if 45<=d[2]<70]
attenzione = [d for d in dati if d[2]<45]

for titolo, lista, bcolor in [
('🟢 SEGNALI OTTIMALI / FAVOREVOLI', ottimali+favorevoli, VERDE),
('🔴 DA MONITORARE / NON FAVOREVOLI', attenzione, TEXT2),
]:
if not lista: continue
tk.Label(inn, text=titolo, font=FT, bg=BG, fg=bcolor).pack(
anchor='w', padx=20, pady=(12,6))

for ruota, r, sm, colm, labm in lista:
c = COL_ST[r['stato']]

card = tk.Frame(inn, bg=BG2,
highlightbackground=colm if sm>=45 else BG4,
highlightthickness=1 if sm>=45 else 0)
card.pack(fill='x', padx=20, pady=4)
tk.Frame(card, bg=colm, width=6).pack(side='left', fill='y')

body_c = tk.Frame(card, bg=BG2)
body_c.pack(side='left', fill='both', expand=True, padx=14, pady=10)

# Riga 1
r1 = tk.Frame(body_c, bg=BG2)
r1.pack(fill='x')
tk.Label(r1, text=f'{ruota} — {r["nome"]}',
font=FB, bg=BG2, fg=TEXT).pack(side='left')
tk.Label(r1, text=labm, font=FSB, bg=BG2, fg=colm).pack(side='left', padx=12)
tk.Label(r1, text=f'Score {sm:.0f}/100',
font=FMO, bg=BG2, fg=ACCENT).pack(side='right')

# Riga 2: metriche compatte
r2 = tk.Frame(body_c, bg=BG2)
r2.pack(fill='x', pady=(4,0))
mets = [
(f'{EMO_ST[r["stato"]]} {r["stato_nome"]}', c),
(f'ICQ {r["icq"]:.1f}', ACCENT),
(f'Run {r["run"]}', TEXT2),
(f'Trend {"↑" if r["trend"]>0.3 else "↓" if r["trend"]<-0.3 else "→"}{r["trend"]:+.1f}',
VERDE if r["trend"]>0.3 else ROSSO if r["trend"]<-0.3 else TEXT3),
(f'Press. {r["pressione"]*100:.0f}%',
ROSSO if r["pressione"]>0.6 else ARANCIO if r["pressione"]>0.35 else VERDE),
]
for mt,mc in mets:
tk.Label(r2, text=mt, font=FSM, bg=BG2, fg=mc).pack(side='left', padx=(0,16))

# Riga 3: top 10 numeri
if sm >= 45:
r3 = tk.Frame(body_c, bg=BG2)
r3.pack(fill='x', pady=(6,0))
tk.Label(r3, text='Top 10:', font=FSB, bg=BG2, fg=TEXT3).pack(side='left', padx=(0,6))
for num,sc in r['numeri'][:10]:
tk.Label(r3, text=str(num), font=('Helvetica',10,'bold'),
bg=colm if sc>=80 else BG3, fg='white' if sc>=80 else TEXT,
width=3, relief='flat',
highlightbackground=BG4, highlightthickness=1).pack(side='left', padx=2)

# Probabilità t+1
best_s = max(r['prob'], key=r['prob'].get)
best_p = r['prob'][best_s]
tk.Label(r3, text=f' → t+1: {EMO_ST[best_s]} {NOM_ST[best_s]} {best_p*100:.0f}%',
font=FSM, bg=BG2, fg=COL_ST[best_s]).pack(side='left', padx=(12,0))

sep(inn, BG3).pack(fill='x', padx=20, pady=16)

# ── Tabella riassuntiva finale ──
tk.Label(inn, text='TABELLA SINOTTICA — tutte le ruote',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(0,8))

tbl = tk.Frame(inn, bg=BG)
tbl.pack(padx=20, anchor='w')

hdrs = ['Ruota','Nome','ICQ','Stato','Run','Trend',
'Score','Pressione','t+1 più prob.','Num 1','Num 2','Num 3']
ws = [6,12,6,12,5,8,7,10,16,6,6,6]
for j,(h,w) in enumerate(zip(hdrs,ws)):
tk.Label(tbl, text=h, font=FSB, bg=BG3, fg=TEXT2,
width=w, anchor='center', pady=5).grid(
row=0, column=j, padx=1, pady=1, sticky='nsew')

for i,(ruota,r,sm,colm,labm) in enumerate(dati):
bg_r = '#FFF7F4' if r['stato']==3 else BG2 if i%2==0 else BG
best_s = max(r['prob'], key=r['prob'].get)
best_p = r['prob'][best_s]
ts = '↑' if r['trend']>0.3 else ('↓' if r['trend']<-0.3 else '→')
pr_str = f'{r["pressione"]*100:.0f}%'
pr_col = ROSSO if r['pressione']>0.6 else ARANCIO if r['pressione']>0.35 else VERDE
top3 = [str(n) for n,_ in r['numeri'][:3]]

row_v = [
ruota, r['nome'], f'{r["icq"]:.1f}',
f'{EMO_ST[r["stato"]]} {r["stato_nome"]}',
str(r['run']),
f'{ts}{r["trend"]:+.1f}',
f'{r["score"]:.1f}', pr_str,
f'{EMO_ST[best_s]} {NOM_ST[best_s]} {best_p*100:.0f}%',
] + top3
fgs = [ACCENT, TEXT, ACCENT, COL_ST[r['stato']],
TEXT, VERDE if r['trend']>0.3 else ROSSO if r['trend']<-0.3 else TEXT3,
ACCENT, pr_col, COL_ST[best_s],
ACCENT, ACCENT, ACCENT]
fnts = [FB,FM,FMO,FSB,FMO,FMO,FMO,FMO,FSB,FB,FB,FB]

for j,(v,w,fg,fn) in enumerate(zip(row_v,ws,fgs,fnts)):
tk.Label(tbl, text=v, font=fn, bg=bg_r, fg=fg,
width=w, anchor='center', pady=4).grid(
row=i+1, column=j, padx=1, pady=1, sticky='nsew')

sep(inn, BG3).pack(fill='x', padx=20, pady=16)

# ── Nota metodologica ──
tk.Label(inn, text='NOTE METODOLOGICHE',
font=FT, bg=BG, fg=TEXT).pack(anchor='w', padx=20, pady=(0,8))

note = [
'1. L\'ICQ misura il CLIMA della casualità, non predice numeri singoli. '
'Le differenze osservate tra stati sono statisticamente significative '
'(ANOVA p<0.001, Ljung-Box p≈0, inerzia +18.5% sul baseline casuale z=31.65).',
'2. La fase CALDA è favorita perché: inerzia 56% (resto nello stesso stato), '
'caratteristiche numeriche distintive (più pari, più fasce estreme), '
'e il sistema ha autocorrelazione reale con r=0.66 al lag 1.',
'3. Il MOMENTO OTTIMALE è il 1°–2° concorso dopo l\'ingresso in Calda '
'con trend positivo: massima probabilità di continuazione, '
'pressione di inversione ancora bassa.',
'4. La selezione numeri NON garantisce la vincita. Offre una composizione '
'statisticamente coerente con il clima corrente. Il Lotto rimane un sistema '
'casuale — questo modello studia i margini di struttura locale.',
]
for nota in note:
nr = tk.Frame(inn, bg=BG2, highlightbackground=BG4, highlightthickness=1)
nr.pack(fill='x', padx=20, pady=3)
tk.Label(nr, text=nota, font=FSM, bg=BG2, fg=TEXT2,
wraplength=900, justify='left').pack(padx=14, pady=8, anchor='w')

# ═══════════════════════════════════════════════════════════════
# VALUTAZIONE MOMENTO
# ═══════════════════════════════════════════════════════════════
def _valuta_momento(self, r):
score = 0
score += r['icq'] * 0.30
if r['trend'] > 0:
score += min(20, r['trend']*4)
if r['stato'] == 3:
if r['run'] <= 2: score += 28
elif r['run'] <= 4: score += 18
elif r['run'] <= 6: score += 8
elif r['stato'] == 4:
score += 12
elif r['stato'] == 2 and r['trend'] > 0.5:
score += 8
score += (1 - r['pressione']) * 15
score = min(100, score)

if r['stato']==3 and r['run']<=2 and r['trend']>0:
col,lab,desc = VERDE,'MOMENTO OTTIMALE', \
f'Ingresso recente in fase CALDA (run={r["run"]} concorsi) con ICQ in salita ' \
f'(trend {r["trend"]:+.2f}). Momento di massima probabilità: 56% di restare Calda, ' \
f'pressione di inversione bassa ({r["pressione"]*100:.0f}%). ' \
f'Applicare la selezione numeri con preferenza per pari e fasce 1–30/61–90.'
elif r['stato']==3 and r['run']<=5:
col,lab,desc = ARANCIO,'MOMENTO FAVOREVOLE', \
f'Fase CALDA in corso da {r["run"]} concorsi. Ancora favorevole ma la pressione ' \
f'di inversione sale ({r["pressione"]*100:.0f}%). ' \
f'Monitorare il trend ICQ ({r["trend"]:+.2f}) ad ogni elaborazione.'
elif r['stato']==3 and r['run']>5:
col,lab,desc = ROSSO,'MOMENTO A RISCHIO', \
f'Fase CALDA prolungata: {r["run"]} concorsi, sopra la media storica (2.28). ' \
f'Pressione di inversione alta ({r["pressione"]*100:.0f}%). ' \
f'Alta probabilità di uscita dalla fase nelle prossime estrazioni.'
elif r['stato']==4:
col,lab,desc = VIOLA,'TURBOLENTA — OPPORTUNITÀ', \
f'Stato Turbolento: massima aggregazione, alta instabilità. ' \
f'Storicamente il 65% delle Turbolente porta alla CALDA al concorso successivo. ' \
f'Possibile ottima opportunità se ICQ scende sotto 85.'
elif r['stato']==2 and r['trend']>0.5:
col,lab,desc = ACC2,'PRE-CALDA — INGRESSO PROBABILE', \
f'Stato Attiva con trend fortemente positivo ({r["trend"]:+.2f}). ' \
f'ICQ a {r["icq"]:.1f}, soglia Calda a 70. ' \
f'Possibile ingresso nella fase ottimale nei prossimi 1–3 concorsi. Prepararsi.'
else:
col,lab,desc = TEXT2,'FASE NON FAVOREVOLE', \
f'Stato {r["stato_nome"]} (ICQ={r["icq"]:.1f}, trend {r["trend"]:+.2f}). ' \
f'Non nella finestra ottimale. Attendere ingresso in CALDA (ICQ>70) ' \
f'con trend positivo prima di applicare la selezione.'

return score, col, lab, desc


# ═══════════════════════════════════════════════════════════════════
# FUNZIONI HELPER UI
# ═══════════════════════════════════════════════════════════════════
def btn(parent, text, cmd, color, state='normal', **kw):
return tk.Button(parent, text=text, font=('Helvetica',10,'bold'),
bg=color, fg='white', activebackground=color,
activeforeground='white', relief='flat', bd=0,
padx=16, pady=7, cursor='hand2',
state=state, command=cmd, **kw)

def sep(parent, color=BG4):
return tk.Frame(parent, bg=color, height=1)

def sep_v(parent, color=BG4):
return tk.Frame(parent, bg=color, width=1)

def clear(frame):
for w in frame.winfo_children():
w.destroy()


# ═══════════════════════════════════════════════════════════════════
if __name__ == '__main__':
app = App()
app.mainloop()
 

Ultima estrazione Lotto

  • Estrazione del lotto
    sabato 20 giugno 2026
    Bari
    90
    24
    74
    14
    75
    Cagliari
    79
    37
    32
    44
    27
    Firenze
    07
    56
    79
    29
    44
    Genova
    17
    24
    43
    89
    22
    Milano
    69
    51
    28
    27
    70
    Napoli
    08
    75
    73
    35
    22
    Palermo
    58
    42
    78
    59
    09
    Roma
    88
    78
    84
    68
    53
    Torino
    61
    34
    65
    59
    27
    Venezia
    37
    23
    76
    89
    86
    Nazionale
    72
    18
    63
    22
    33
    Estrazione Simbolotto
    Napoli
    22
    29
    25
    24
    43

Ultimi Messaggi

Indietro
Alto