File size: 8,854 Bytes
dda5a83
87d5a96
3e8496e
7f2edaf
86b253a
dda5a83
 
 
 
 
74a5ed9
87d5a96
 
 
 
 
 
08dda9e
87d5a96
 
a47d1ea
87d5a96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a47d1ea
dda5a83
87d5a96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a47d1ea
87d5a96
dda5a83
436e4a3
87d5a96
7f2edaf
87d5a96
7f2edaf
436e4a3
 
87d5a96
7f2edaf
3e8496e
dda5a83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7f2edaf
dda5a83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a47d1ea
dda5a83
 
 
 
 
 
 
87d5a96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dda5a83
87d5a96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74a5ed9
87d5a96
 
 
 
 
 
 
dda5a83
87d5a96
 
 
 
 
 
 
 
 
 
 
 
dda5a83
87d5a96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dda5a83
87d5a96
21ab5eb
87d5a96
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import streamlit as st
import os
import yfinance as yf
import pandas as pd
import numpy as np
import torch
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from datetime import datetime, timedelta

# Configurazione iniziale di Streamlit
st.set_page_config(
    page_title="Financial Prediction App",
    layout="wide",
    initial_sidebar_state="expanded"
)

# Disabilita i warning di parallelismo
os.environ["TOKENIZERS_PARALLELISM"] = "false"

# Variabili globali per il modello
tokenizer = None
model = None

@st.cache_resource(show_spinner="Caricamento del modello FinBERT...")
def load_finbert():
    global tokenizer, model
    try:
        from transformers import BertTokenizer, BertForSequenceClassification
        
        model_name = "yiyanghkust/finbert-tone"
        tokenizer = BertTokenizer.from_pretrained(model_name)
        model = BertForSequenceClassification.from_pretrained(model_name)
        model.eval()  # Imposta il modello in modalità valutazione
        
        return tokenizer, model
    except Exception as e:
        st.error(f"Errore nel caricamento del modello: {str(e)}")
        return None, None

def analyze_sentiment(text):
    global tokenizer, model
    if tokenizer is None or model is None:
        return "Modello non caricato", [0, 0, 0]
    
    try:
        inputs = tokenizer(text, return_tensors="pt", truncation=True, padding=True, max_length=512)
        with torch.no_grad():
            outputs = model(**inputs)
        predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
        sentiment = torch.argmax(predictions).item()
        sentiment_map = {0: "Negative", 1: "Neutral", 2: "Positive"}
        return sentiment_map[sentiment], predictions[0].tolist()
    except Exception as e:
        st.error(f"Errore nell'analisi del sentiment: {str(e)}")
        return "Errore", [0, 0, 0]

@st.cache_data(ttl=3600, show_spinner="Download dati storici...")
def get_historical_data(symbol, period="1y"):
    try:
        data = yf.download(symbol, period=period, progress=False)
        if data.empty:
            st.warning(f"Nessun dato trovato per {symbol}")
            return None
        return data
    except Exception as e:
        st.error(f"Errore nel download dei dati: {str(e)}")
        return None

def prepare_data(data, sentiment_score=None):
    # Feature engineering
    data['Return'] = data['Close'].pct_change()
    data['MA_7'] = data['Close'].rolling(window=7).mean()
    data['MA_30'] = data['Close'].rolling(window=30).mean()
    data['Volatility'] = data['Close'].rolling(window=7).std()
    
    # Aggiunta del sentiment se disponibile
    if sentiment_score is not None:
        data['Sentiment'] = sentiment_score
    
    # Rimozione dei valori NaN
    data.dropna(inplace=True)
    
    # Selezione delle feature
    features = ['Open', 'High', 'Low', 'Close', 'Volume', 'Return', 'MA_7', 'MA_30', 'Volatility']
    if sentiment_score is not None:
        features.append('Sentiment')
    
    X = data[features]
    y = data['Close'].shift(-1)  # Prevedere il prezzo di chiusura del giorno successivo
    
    # Rimuovi l'ultima riga senza target
    X = X[:-1]
    y = y[:-1]
    
    return X, y

def train_and_predict(X, y, forecast_days=7):
    # Normalizzazione dei dati
    scaler = MinMaxScaler()
    X_scaled = scaler.fit_transform(X)
    
    # Divisione dei dati
    X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)
    
    # Addestramento del modello
    model = RandomForestRegressor(n_estimators=100, random_state=42)
    model.fit(X_train, y_train)
    
    # Previsione per i prossimi giorni
    last_values = X.iloc[-1:].values
    last_values_scaled = scaler.transform(last_values)
    
    predictions = []
    current_input = last_values_scaled.copy()
    
    for _ in range(forecast_days):
        pred = model.predict(current_input)[0]
        predictions.append(pred)
        
        # Aggiornamento dell'input per la previsione successiva
        new_row = current_input[0].copy()
        new_row[3] = pred  # Aggiorna il prezzo di chiusura
        current_input = np.array([new_row])
    
    return predictions

def initialize_app():
    st.title("📈 Financial Prediction App with FinBERT")
    st.markdown("### Previsioni finanziarie per azioni, forex, commodities, crypto e indici")
    
    # Carica il modello
    global tokenizer, model
    if tokenizer is None or model is None:
        with st.spinner("Caricamento del modello..."):
            tokenizer, model = load_finbert()
    
    if tokenizer is None or model is None:
        st.error("Impossibile caricare il modello. Riprova più tardi.")
        st.stop()
    
    return True

def main():
    # Inizializza l'app
    if not initialize_app():
        return
    
    # Sidebar per le impostazioni
    st.sidebar.header("Impostazioni")
    asset_type = st.sidebar.selectbox(
        "Seleziona tipo di asset",
        ["Azioni", "Forex", "Commodities", "Crypto", "Indici"]
    )
    
    # Mappatura dei simboli per tipo di asset
    symbol_map = {
        "Azioni": ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA"],
        "Forex": ["EURUSD=X", "GBPUSD=X", "USDJPY=X", "USDCHF=X"],
        "Commodities": ["GC=F", "SI=F", "CL=F", "NG=F"],
        "Crypto": ["BTC-USD", "ETH-USD", "BNB-USD", "SOL-USD"],
        "Indici": ["^GSPC", "^DJI", "^IXIC", "^FTSE"]
    }
    
    symbol = st.sidebar.selectbox("Seleziona simbolo", symbol_map[asset_type])
    period = st.sidebar.selectbox("Periodo storico", ["1mo", "3mo", "6mo", "1y", "2y"])
    forecast_days = st.sidebar.slider("Giorni di previsione", 1, 30, 7)
    
    # Input per l'analisi del sentiment
    st.subheader("Analisi del Sentiment con FinBERT")
    news_text = st.text_area("Inserisci notizie finanziarie (opzionale):", height=150)
    
    # Bottone per eseguire l'analisi
    if st.button("Genera Previsioni"):
        with st.spinner("Analisi in corso..."):
            # Analisi del sentiment
            sentiment = None
            sentiment_scores = None
            
            if news_text:
                sentiment, sentiment_scores = analyze_sentiment(news_text)
                st.success(f"Sentiment rilevato: {sentiment}")
                st.write("Score dettagliati:")
                st.write(f"Negativo: {sentiment_scores[0]:.4f}")
                st.write(f"Neutro: {sentiment_scores[1]:.4f}")
                st.write(f"Positivo: {sentiment_scores[2]:.4f}")
                
                # Conversione del sentiment in punteggio numerico
                sentiment_score = sentiment_scores[2] - sentiment_scores[0]  # Positivo - Negativo
            else:
                st.info("Nessuna notizia inserita. Verrà utilizzato solo l'analisi tecnica.")
                sentiment_score = None
            
            # Download dei dati storici
            data = get_historical_data(symbol, period)
            
            if data is not None:
                # Preparazione dei dati
                X, y = prepare_data(data, sentiment_score)
                
                if len(X) > 10:  # Verifica di avere dati sufficienti
                    # Addestramento e previsione
                    predictions = train_and_predict(X, y, forecast_days)
                    
                    # Creazione del dataframe delle previsioni
                    last_date = data.index[-1]
                    prediction_dates = [last_date + timedelta(days=i) for i in range(1, forecast_days+1)]
                    predictions_df = pd.DataFrame({
                        "Data": prediction_dates,
                        "Previsione": predictions
                    })
                    
                    # Visualizzazione dei risultati
                    st.subheader(f"Previsioni per {symbol} ({asset_type})")
                    st.write(predictions_df)
                    
                    # Grafico
                    st.line_chart(data['Close'].tail(30))
                    st.line_chart(predictions_df.set_index("Data"))
                    
                    # Statistiche del modello
                    st.subheader("Statistiche del Modello")
                    st.write(f"Numero di campioni di training: {len(X)}")
                    st.write(f"Feature utilizzate: {list(X.columns)}")
                    if sentiment_score is not None:
                        st.write(f"Punteggio di sentiment utilizzato: {sentiment_score:.4f}")
                else:
                    st.error("Dati insufficienti per generare previsioni. Seleziona un periodo più lungo.")
            else:
                st.error("Impossibile recuperare i dati per il simbolo selezionato.")

if __name__ == "__main__":
    main()