---
title: Forecast Stock Prices with LSTM in Python: Complete Project Guide from Data Prep to Deployment
siteUrl: https://logzly.com/mltutorialhub
author: mltutorialhub (ML Tutorial Hub)
date: 2026-06-15T20:34:59.888755
tags: [stock, machinelearning, python]
url: https://logzly.com/mltutorialhub/forecast-stock-prices-with-lstm-in-python-complete-project-guide-from-data-prep-to-deployment
---


**Disclosure: We are reader supported, and earn affiliate commissions when you buy through us.**


If you’ve ever watched a news anchor brag about “the next big thing” in the market and thought, “I could have seen that coming,” you’re not alone. [Predicting stock prices](https://www.amazon.com/s?k=predicting+stock+prices&tag=organizationtip101-20) feels like trying to read the future, but with the [right tools](https://www.amazon.com/s?k=right+tools&tag=organizationtip101-20)—like an LSTM network—you can turn a hunch into a data‑driven guess that actually makes sense.

## Why LSTM?

Long Short‑Term Memory (LSTM) networks belong to the family of [recurrent neural networks](https://www.amazon.com/s?k=recurrent+neural+networks&tag=organizationtip101-20) (RNNs). In plain English, they are designed to remember patterns over time. [Stock prices](https://www.amazon.com/s?k=stock+prices&tag=organizationtip101-20) are a classic time‑series problem: today’s price depends on yesterday’s, last week’s, maybe even last year’s trends. Traditional feed‑forward models forget that order, but LSTMs keep a “memory” of past values, making them a natural fit for [financial forecasting](https://www.amazon.com/s?k=financial+forecasting&tag=organizationtip101-20). If you’re curious about how other [neural network architectures](https://www.amazon.com/s?k=neural+network+architectures&tag=organizationtip101-20) are applied, see our guide on **[building a real‑world image classifier with PyTorch transfer learning](/mltutorialhub/how-to-build-a-real-world-image-classifier-with-pytorch-transfer-learning)**.

## The Project Roadmap

Below is the step‑by‑step flow we’ll follow:

1. Gather [historical price data](https://www.amazon.com/s?k=Historical+Price+Data&tag=organizationtip101-20)  
2. Clean and scale the data  
3. Create sequences for the LSTM  
4. Build and train the model  
5. Evaluate performance  
6. Deploy as a simple Flask API  

Let’s dive in.

## 1. [Data Collection](https://www.amazon.com/s?k=data+collection&tag=organizationtip101-20)

For a quick demo I used [Yahoo Finance](https://www.amazon.com/s?k=Yahoo+Finance&tag=organizationtip101-20)’s [free API](https://www.amazon.com/s?k=free+API&tag=organizationtip101-20) via the `yfinance` [Python package](https://www.amazon.com/s?k=Python+package&tag=organizationtip101-20). It’s a handy way to pull daily OHLCV (Open, High, Low, Close, Volume) data without signing up for a paid service.

```python
import yfinance as yf
import pandas as pd

ticker = "AAPL"
df = yf.download(ticker, start="2015-01-01", end="2024-01-01")
df = df[['Close']]          # we only need closing price for now
df.head()
```

The resulting DataFrame has a Date index and a single column called `Close`. Feel free to add other columns later; they can improve the model, but they also add complexity.

## 2. Data Preparation

### 2.1 [Handling Missing Values](https://www.amazon.com/s?k=handling+missing+values&tag=organizationtip101-20)

[Financial data](https://www.amazon.com/s?k=financial+data&tag=organizationtip101-20) sometimes has gaps (holidays, market closures). A simple forward‑fill works well:

```python
df.ffill(inplace=True)
```

### 2.2 Scaling

[Neural networks](https://www.amazon.com/s?k=neural+networks&tag=organizationtip101-20) love numbers that sit roughly between 0 and 1. We’ll use `MinMaxScaler` from scikit‑learn.

```python
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
scaled = scaler.fit_transform(df)
```

### 2.3 Train‑Test Split

We keep the most recent 20% of data for testing, mimicking a real‑world scenario where the model predicts future values.

```python
train_size = int(len(scaled) * 0.8)
train, test = scaled[:train_size], scaled[train_size:]
```

## 3. Creating Sequences

LSTMs need input shaped as **samples × timesteps × features**. We’ll use a sliding window of 60 days (about three months) to predict the next day’s price.

```python
import numpy as np

def create_sequences(data, window=60):
    X, y = [], []
    for i in range(len(data) - window):
        X.append(data[i:i+window])
        y.append(data[i+window])
    return np.array(X), np.array(y)

X_train, y_train = create_sequences(train)
X_test,  y_test  = create_sequences(test)
```

Now `X_train` has shape `(num_samples, 60, 1)`—the extra dimension is for the single feature (Close price).

## 4. Building the LSTM Model

I like to keep the architecture simple: one LSTM layer followed by a dense output layer.

```python
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout

model = Sequential([
    LSTM(50, return_sequences=False, input_shape=(X_train.shape[1], 1)),
    Dropout(0.2),
    Dense(1)
])

model.compile(optimizer='adam', loss='mean_squared_error')
model.summary()
```

The `return_sequences=False` flag tells the LSTM to output only the last hidden state, which is enough for a single‑step forecast. The dropout layer helps prevent over‑fitting—think of it as a gentle reminder to the model not to memorize the [training data](https://www.amazon.com/s?k=training+data&tag=organizationtip101-20).

## 5. [Training the Model](https://www.amazon.com/s?k=Training+the+Model&tag=organizationtip101-20)

Training for 30 epochs usually gives a decent result for this toy example. Adjust the number based on your hardware and patience.

```python
history = model.fit(
    X_train, y_train,
    epochs=30,
    batch_size=32,
    validation_split=0.1,
    verbose=1
)
```

You’ll see the loss decreasing on both training and validation sets. If validation loss starts rising while training loss keeps falling, you’re over‑fitting—time to add more dropout or reduce epochs.

## 6. Evaluation

First, we predict on the test set and then invert the scaling to get actual price values.

```python
pred_scaled = model.predict(X_test)
pred = scaler.inverse_transform(pred_scaled)
actual = scaler.inverse_transform(y_test)
```

A quick visual check:

```python
import matplotlib.pyplot as plt

plt.figure(figsize=(12,5))
plt.plot(actual, label='Actual')
plt.plot(pred,   label='Predicted')
plt.title('AAPL Close Price: Actual vs Predicted')
plt.legend()
plt.show()
```

You’ll notice the lines are close but not perfect—[stock markets](https://www.amazon.com/s?k=stock+markets&tag=organizationtip101-20) are noisy! A common metric is Root [Mean Squared Error](https://www.amazon.com/s?k=mean+squared+error&tag=organizationtip101-20) (RMSE). Lower is better.

```python
from sklearn.metrics import mean_squared_error
import math

rmse = math.sqrt(mean_squared_error(actual, pred))
print(f'RMSE: {rmse:.2f}')
```

If the RMSE is within a few dollars for a high‑priced stock, you’ve done a respectable job for a first pass.

## 7. Deploying the Model

### 7.1 Saving the Model

```python
model.save('lstm_stock_model.h5')
import joblib
joblib.dump(scaler, 'scaler.save')
```

### 7.2 Simple Flask API

Below is a minimal Flask app that loads the model and returns a one‑day‑ahead forecast for a given ticker. In practice you’d add error handling, logging, and maybe a caching layer.

```python
from flask import Flask, request, jsonify
import tensorflow as tf
import numpy as np
import joblib
import yfinance as yf

app = Flask(__name__)

model = tf.keras.models.load_model('lstm_stock_model.h5')
scaler = joblib.load('scaler.save')
WINDOW = 60

def get_recent_prices(ticker):
    df = yf.download(ticker, period='90d')[['Close']]
    df.ffill(inplace=True)
    return scaler.transform(df)

@app.route('/predict', methods=['GET'])
def predict():
    ticker = request.args.get('ticker', default='AAPL')
    data = get_recent_prices(ticker)
    recent_seq = data[-WINDOW:].reshape(1, WINDOW, 1)
    pred_scaled = model.predict(recent_seq)
    pred_price = scaler.inverse_transform(pred_scaled)[0][0]
    return jsonify({'ticker': ticker, 'predicted_close': round(pred_price, 2)})

if __name__ == '__main__':
    app.run(debug=True)
```

Run the script, hit `http://127.0.0.1:5000/predict?ticker=MSFT`, and you’ll get a JSON response with tomorrow’s predicted closing price. It’s a fun way to turn a notebook experiment into a tiny [web service](https://www.amazon.com/s?k=web+service&tag=organizationtip101-20). If you prefer a visual deep‑learning project, you might also enjoy our **[step‑by‑step tutorial on creating an image classifier in PyTorch](/mltutorialhub/build-a-real-world-image-classifier-in-pytorch-a-step-by-step-tutorial-for-beginners)**.

## 8. Things to Keep in Mind

- **Data leakage**: Never let future data sneak into the training set. The sliding window approach helps avoid this.
- **[Feature engineering](https://www.amazon.com/s?k=feature+engineering&tag=organizationtip101-20)**: Adding [technical indicators](https://www.amazon.com/s?k=technical+indicators&tag=organizationtip101-20) ([moving averages](https://www.amazon.com/s?k=moving+averages&tag=organizationtip101-20), RSI) can boost performance, but they also increase the risk of over‑fitting.
- **Model stability**: Stock markets are influenced by news, earnings, and macro events—none of which are captured in price history alone. Treat the model as a guide, not a crystal ball.
- **Regulation**: If you plan to use predictions for real trading, check the [legal requirements](https://www.amazon.com/s?k=legal+requirements&tag=organizationtip101-20) in your jurisdiction.

For a broader view of image‑classification pipelines, see our **[comprehensive PyTorch guide](/mltutorialhub/build-a-realworld-image-classifier-with-pytorch-a-stepbystep-tutorial)**.

That’s it—a full walk‑through from raw [price data](https://www.amazon.com/s?k=price+data&tag=organizationtip101-20) to a deployable LSTM service. I hope this project gives you a [solid foundation](https://www.amazon.com/s?k=solid+foundation&tag=organizationtip101-20) to experiment further, perhaps by trying bidirectional LSTMs, attention mechanisms, or even [transformer models](https://www.amazon.com/s?k=Transformer+models&tag=organizationtip101-20). The world of finance is noisy, but with clear code and a disciplined workflow, you can turn that noise into insight.
