pyroleli commited on
Commit
ec84a63
·
verified ·
1 Parent(s): 7159400

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +187 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,189 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import yfinance as yf
3
+ import pandas as pd
4
+ import numpy as np
5
+ import tensorflow as tf
6
+ from tensorflow.keras.models import Sequential
7
+ from tensorflow.keras.layers import LSTM, Dense
8
+ from sklearn.preprocessing import MinMaxScaler
9
+ from sklearn.metrics import mean_squared_error
10
+ import plotly.graph_objects as go
11
+ from datetime import date, timedelta
12
+
13
+ # --- CONFIGURATION ---
14
+ st.set_page_config(layout="wide", page_title="AI Stock Predictor")
15
+
16
+ # --- UI HEADER ---
17
+ st.title("📈 Neural Network Stock Predictor")
18
+ st.markdown("""
19
+ This app uses a **Long Short-Term Memory (LSTM)** neural network to predict stock prices.
20
+ It first **simulates** the model against the last year's data to verify accuracy, then predicts the future.
21
+ """)
22
+
23
+ # --- SIDEBAR DASHBOARD ---
24
+ st.sidebar.header("Configuration")
25
+ ticker = st.sidebar.text_input("Enter Ticker Symbol", value="^IXIC") # Default to NASDAQ
26
+ st.sidebar.caption("Examples: ^IXIC (Nasdaq), AAPL, TSLA, BTC-USD")
27
+
28
+ horizon_option = st.sidebar.selectbox(
29
+ "Prediction Horizon",
30
+ ("Next Day", "Next Week", "Next Month", "Next Year")
31
+ )
32
+
33
+ # Map horizon to days
34
+ horizon_mapping = {
35
+ "Next Day": 1,
36
+ "Next Week": 7,
37
+ "Next Month": 30,
38
+ "Next Year": 365
39
+ }
40
+ forecast_days = horizon_mapping[horizon_option]
41
+
42
+ # --- FUNCTIONS ---
43
+
44
+ @st.cache_data
45
+ def load_data(symbol):
46
+ """Fetches data from yfinance. We fetch 5 years to ensure enough training data."""
47
+ start_date = date.today() - timedelta(days=5*365)
48
+ data = yf.download(symbol, start=start_date, end=date.today())
49
+ data.reset_index(inplace=True)
50
+ return data
51
+
52
+ def create_dataset(dataset, look_back=60):
53
+ """Converts array of values into a dataset matrix for LSTM."""
54
+ dataX, dataY = [], []
55
+ for i in range(len(dataset) - look_back - 1):
56
+ a = dataset[i:(i + look_back), 0]
57
+ dataX.append(a)
58
+ dataY.append(dataset[i + look_back, 0])
59
+ return np.array(dataX), np.array(dataY)
60
+
61
+ def train_lstm_model(train_data, look_back=60):
62
+ """Builds and trains the LSTM Neural Network."""
63
+ # Reshape input to be [samples, time steps, features]
64
+ X_train, y_train = create_dataset(train_data, look_back)
65
+ X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
66
+
67
+ # Build LSTM Architecture
68
+ model = Sequential()
69
+ model.add(LSTM(50, return_sequences=True, input_shape=(look_back, 1)))
70
+ model.add(LSTM(50, return_sequences=False))
71
+ model.add(Dense(25))
72
+ model.add(Dense(1)) # Output layer
73
+
74
+ model.compile(optimizer='adam', loss='mean_squared_error')
75
+
76
+ # Train (Epochs=1 is used here for speed in demo, increase to 20-50 for real accuracy)
77
+ model.fit(X_train, y_train, batch_size=1, epochs=1, verbose=0)
78
+ return model
79
+
80
+ # --- MAIN EXECUTION ---
81
+
82
+ data_load_state = st.text('Loading data...')
83
+ try:
84
+ data = load_data(ticker)
85
+ data_load_state.text('Loading data... done!')
86
+ except Exception as e:
87
+ st.error(f"Error loading data: {e}")
88
+ st.stop()
89
+
90
+ if len(data) < 500:
91
+ st.error("Not enough data to train the model. Please choose a stock with deeper history.")
92
+ st.stop()
93
+
94
+ # Prepare Data
95
+ df_close = data[['Close']].values
96
+ scaler = MinMaxScaler(feature_range=(0, 1))
97
+ scaled_data = scaler.fit_transform(df_close)
98
+
99
+ # --- SIMULATION (BACKTESTING) ---
100
+ st.subheader("1. Simulation: Testing against Last Year")
101
+ st.write("Training model on past data to verify performance on the last 365 days...")
102
+
103
+ # Split data: Train on everything BEFORE the last 365 days, Test on LAST 365 days
104
+ training_len = len(scaled_data) - 365
105
+ train_data = scaled_data[0:training_len, :]
106
+ test_data = scaled_data[training_len - 60:, :] # -60 to handle look_back
107
+
108
+ # Train Model
109
+ with st.spinner('Training Neural Network... (This may take a moment)'):
110
+ model = train_lstm_model(train_data)
111
+
112
+ # Predict on the "Last Year" (Simulation)
113
+ x_test = []
114
+ look_back = 60
115
+ for i in range(60, len(test_data)):
116
+ x_test.append(test_data[i-60:i, 0])
117
+ x_test = np.array(x_test)
118
+ x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))
119
+
120
+ predictions = model.predict(x_test)
121
+ predictions = scaler.inverse_transform(predictions) # Scale back to normal price
122
+
123
+ # Calculate Accuracy (RMSE)
124
+ valid_set = data[training_len:]
125
+ valid_set['Predictions'] = predictions
126
+ rmse = np.sqrt(np.mean(((predictions - valid_set['Close'].values) ** 2)))
127
+
128
+ # Calculate Directional Accuracy (Did it go up/down correctly?)
129
+ valid_set['Actual_Change'] = valid_set['Close'].diff()
130
+ valid_set['Pred_Change'] = valid_set['Predictions'].diff()
131
+ valid_set['Correct_Direction'] = np.sign(valid_set['Actual_Change']) == np.sign(valid_set['Pred_Change'])
132
+ accuracy_score = valid_set['Correct_Direction'].mean() * 100
133
+
134
+ col1, col2 = st.columns(2)
135
+ col1.metric("Simulation RMSE (Price Error)", f"{rmse:.2f}")
136
+ col2.metric("Directional Accuracy", f"{accuracy_score:.2f}%")
137
+
138
+ if accuracy_score > 50:
139
+ st.success(f"Model passed simulation with {accuracy_score:.1f}% directional accuracy.")
140
+ else:
141
+ st.warning(f"Model accuracy is low ({accuracy_score:.1f}%). Stock markets are volatile!")
142
+
143
+ # Plot Simulation
144
+ fig_sim = go.Figure()
145
+ fig_sim.add_trace(go.Scatter(x=data['Date'][:training_len], y=data['Close'][:training_len].values.flatten(), mode='lines', name='Training Data'))
146
+ fig_sim.add_trace(go.Scatter(x=valid_set['Date'], y=valid_set['Close'].values.flatten(), mode='lines', name='Actual Price (Last Year)'))
147
+ fig_sim.add_trace(go.Scatter(x=valid_set['Date'], y=valid_set['Predictions'].values.flatten(), mode='lines', name='AI Prediction (Simulation)', line=dict(dash='dot', color='orange')))
148
+ st.plotly_chart(fig_sim, use_container_width=True)
149
+
150
+
151
+ # --- FUTURE PREDICTION ---
152
+ st.markdown("---")
153
+ st.subheader(f"2. Future Forecast: {horizon_option}")
154
+
155
+ # Retrain model on ALL data for best future prediction
156
+ with st.spinner('Refining model with full data for future prediction...'):
157
+ full_model = train_lstm_model(scaled_data)
158
+
159
+ # Predict Future Steps
160
+ # We start with the last 60 days of known data
161
+ last_60_days = scaled_data[-60:]
162
+ current_batch = last_60_days.reshape((1, 60, 1))
163
+ future_predictions = []
164
+
165
+ for i in range(forecast_days):
166
+ # Get prediction (scaled)
167
+ current_pred = full_model.predict(current_batch)[0]
168
+ future_predictions.append(current_pred)
169
+
170
+ # Update batch to include new prediction, remove oldest day
171
+ current_pred_reshaped = current_pred.reshape((1, 1, 1))
172
+ current_batch = np.append(current_batch[:, 1:, :], current_pred_reshaped, axis=1)
173
+
174
+ # Inverse transform to get real prices
175
+ future_predictions = scaler.inverse_transform(future_predictions)
176
+
177
+ # Create Future Dates
178
+ last_date = data['Date'].iloc[-1]
179
+ future_dates = [last_date + timedelta(days=x) for x in range(1, forecast_days + 1)]
180
+
181
+ # Plot Future
182
+ fig_future = go.Figure()
183
+ # Show last 365 days of context
184
+ fig_future.add_trace(go.Scatter(x=data['Date'][-365:], y=data['Close'][-365:].values.flatten(), mode='lines', name='Historical Close (Last Year)'))
185
+ fig_future.add_trace(go.Scatter(x=future_dates, y=future_predictions.flatten(), mode='lines', name='AI Future Prediction', line=dict(dash='dot', color='green', width=3)))
186
+ fig_future.update_layout(title=f"Prediction for next {forecast_days} days")
187
+ st.plotly_chart(fig_future, use_container_width=True)
188
 
189
+ st.write("Note: Long-term predictions (Year) usually revert to a trend line as error accumulates. Short-term (Day/Week) is generally more reliable.")