File size: 7,330 Bytes
78c4046
ec84a63
 
 
358704d
 
 
ec84a63
358704d
ec84a63
358704d
ec84a63
 
358704d
ec84a63
 
358704d
ec84a63
358704d
 
ec84a63
 
 
 
358704d
 
ec84a63
4b90bc1
358704d
 
4b90bc1
358704d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec84a63
 
 
358704d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec84a63
358704d
 
ec84a63
 
358704d
ec84a63
358704d
 
 
 
 
 
ec84a63
 
358704d
 
 
4b90bc1
358704d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4b90bc1
 
358704d
 
4b90bc1
358704d
 
 
 
 
 
ec84a63
358704d
 
 
ec84a63
 
358704d
ec84a63
 
 
 
358704d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec84a63
358704d
 
 
ec84a63
358704d
ec84a63
358704d
 
 
 
 
 
 
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
import streamlit as st
import yfinance as yf
import pandas as pd
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
import plotly.graph_objects as go
from datetime import date, timedelta

# --- CONFIGURATION ---
st.set_page_config(layout="wide", page_title="AI Stock Predictor")

# --- UI HEADER ---
st.title("📈 Neural Network Stock Predictor")
st.markdown("""
This app uses a **Long Short-Term Memory (LSTM)** neural network to predict stock prices. 
It first **simulates** the model against the last year's data to verify accuracy, then predicts the future.
""")

# --- SIDEBAR DASHBOARD ---
st.sidebar.header("Configuration")
ticker = st.sidebar.text_input("Enter Ticker Symbol", value="^IXIC") # Default to NASDAQ
st.sidebar.caption("Examples: ^IXIC (Nasdaq), AAPL, TSLA, BTC-USD")

horizon_option = st.sidebar.selectbox(
    "Prediction Horizon", 
    ("Next Day", "Next Week", "Next Month", "Next Year")
)

# Map horizon to days
horizon_mapping = {
    "Next Day": 1,
    "Next Week": 7,
    "Next Month": 30,
    "Next Year": 365
}
forecast_days = horizon_mapping[horizon_option]

# --- FUNCTIONS ---

@st.cache_data
def load_data(symbol):
    """Fetches data from yfinance. We fetch 5 years to ensure enough training data."""
    start_date = date.today() - timedelta(days=5*365)
    data = yf.download(symbol, start=start_date, end=date.today())
    data.reset_index(inplace=True)
    return data

def create_dataset(dataset, look_back=60):
    """Converts array of values into a dataset matrix for LSTM."""
    dataX, dataY = [], []
    for i in range(len(dataset) - look_back - 1):
        a = dataset[i:(i + look_back), 0]
        dataX.append(a)
        dataY.append(dataset[i + look_back, 0])
    return np.array(dataX), np.array(dataY)

def train_lstm_model(train_data, look_back=60):
    """Builds and trains the LSTM Neural Network."""
    # Reshape input to be [samples, time steps, features]
    X_train, y_train = create_dataset(train_data, look_back)
    X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
    
    # Build LSTM Architecture
    model = Sequential()
    model.add(LSTM(50, return_sequences=True, input_shape=(look_back, 1)))
    model.add(LSTM(50, return_sequences=False))
    model.add(Dense(25))
    model.add(Dense(1)) # Output layer
    
    model.compile(optimizer='adam', loss='mean_squared_error')
    
    # Train (Epochs=1 is used here for speed in demo, increase to 20-50 for real accuracy)
    model.fit(X_train, y_train, batch_size=1, epochs=1, verbose=0)
    return model

# --- MAIN EXECUTION ---

data_load_state = st.text('Loading data...')
try:
    data = load_data(ticker)
    data_load_state.text('Loading data... done!')
except Exception as e:
    st.error(f"Error loading data: {e}")
    st.stop()

if len(data) < 500:
    st.error("Not enough data to train the model. Please choose a stock with deeper history.")
    st.stop()

# Prepare Data
df_close = data[['Close']].values
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(df_close)

# --- SIMULATION (BACKTESTING) ---
st.subheader("1. Simulation: Testing against Last Year")
st.write("Training model on past data to verify performance on the last 365 days...")

# Split data: Train on everything BEFORE the last 365 days, Test on LAST 365 days
training_len = len(scaled_data) - 365
train_data = scaled_data[0:training_len, :]
test_data = scaled_data[training_len - 60:, :] # -60 to handle look_back

# Train Model
with st.spinner('Training Neural Network... (This may take a moment)'):
    model = train_lstm_model(train_data)

# Predict on the "Last Year" (Simulation)
x_test = []
look_back = 60
for i in range(60, len(test_data)):
    x_test.append(test_data[i-60:i, 0])
x_test = np.array(x_test)
x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))

predictions = model.predict(x_test)
predictions = scaler.inverse_transform(predictions) # Scale back to normal price

# Calculate Accuracy (RMSE)
valid_set = data[training_len:]
valid_set['Predictions'] = predictions
rmse = np.sqrt(np.mean(((predictions - valid_set['Close'].values) ** 2)))

# Calculate Directional Accuracy (Did it go up/down correctly?)
valid_set['Actual_Change'] = valid_set['Close'].diff()
valid_set['Pred_Change'] = valid_set['Predictions'].diff()
valid_set['Correct_Direction'] = np.sign(valid_set['Actual_Change']) == np.sign(valid_set['Pred_Change'])
accuracy_score = valid_set['Correct_Direction'].mean() * 100

col1, col2 = st.columns(2)
col1.metric("Simulation RMSE (Price Error)", f"{rmse:.2f}")
col2.metric("Directional Accuracy", f"{accuracy_score:.2f}%")

if accuracy_score > 50:
    st.success(f"Model passed simulation with {accuracy_score:.1f}% directional accuracy.")
else:
    st.warning(f"Model accuracy is low ({accuracy_score:.1f}%). Stock markets are volatile!")

# Plot Simulation
fig_sim = go.Figure()
fig_sim.add_trace(go.Scatter(x=data['Date'][:training_len], y=data['Close'][:training_len].values.flatten(), mode='lines', name='Training Data'))
fig_sim.add_trace(go.Scatter(x=valid_set['Date'], y=valid_set['Close'].values.flatten(), mode='lines', name='Actual Price (Last Year)'))
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')))
st.plotly_chart(fig_sim, use_container_width=True)


# --- FUTURE PREDICTION ---
st.markdown("---")
st.subheader(f"2. Future Forecast: {horizon_option}")

# Retrain model on ALL data for best future prediction
with st.spinner('Refining model with full data for future prediction...'):
    full_model = train_lstm_model(scaled_data)

# Predict Future Steps
# We start with the last 60 days of known data
last_60_days = scaled_data[-60:]
current_batch = last_60_days.reshape((1, 60, 1))
future_predictions = []

for i in range(forecast_days):
    # Get prediction (scaled)
    current_pred = full_model.predict(current_batch)[0]
    future_predictions.append(current_pred)
    
    # Update batch to include new prediction, remove oldest day
    current_pred_reshaped = current_pred.reshape((1, 1, 1))
    current_batch = np.append(current_batch[:, 1:, :], current_pred_reshaped, axis=1)

# Inverse transform to get real prices
future_predictions = scaler.inverse_transform(future_predictions)

# Create Future Dates
last_date = data['Date'].iloc[-1]
future_dates = [last_date + timedelta(days=x) for x in range(1, forecast_days + 1)]

# Plot Future
fig_future = go.Figure()
# Show last 365 days of context
fig_future.add_trace(go.Scatter(x=data['Date'][-365:], y=data['Close'][-365:].values.flatten(), mode='lines', name='Historical Close (Last Year)'))
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)))
fig_future.update_layout(title=f"Prediction for next {forecast_days} days")
st.plotly_chart(fig_future, use_container_width=True)

st.write("Note: Long-term predictions (Year) usually revert to a trend line as error accumulates. Short-term (Day/Week) is generally more reliable.")