Stock Market Prediction¶
In this example, we'll be exploring how we can use Linear Regression to predict stock prices thirty days into the future.
You probably won't get rich with this algorithm, but it is still rather satisfying to watch your computer predict the price of your favourite stock.
Getting started¶
Work through the notebook cell by cell; if you prefer a script, copy the code into a new stock_forecast.py file and run it from this folder.
We need a few dependencies. If you do not have them installed, run pip install <dependency> on the command line.
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn import preprocessing
Stock data & dataframe¶
We use Meta Platforms (ticker META, the company formerly known as Facebook). A snapshot of roughly ten years of daily adjusted close prices is stored next to this notebook in data/META_daily.csv, so the notebook runs offline and always gives the same numbers in class.
(Adjusted close accounts for splits and dividends, which is what we want when we model a price series.)
csv_path = Path("data") / "META_daily.csv"
df = pd.read_csv(csv_path, index_col="Date", parse_dates=True)
print(f"{len(df)} trading days, {df.index.min().date()} to {df.index.max().date()}")
df.tail()
2514 trading days, 2016-09-12 to 2026-09-11
| Adj. Close | |
|---|---|
| Date | |
| 2026-09-04 | 616.770020 |
| 2026-09-08 | 613.479980 |
| 2026-09-09 | 653.690002 |
| 2026-09-10 | 644.380005 |
| 2026-09-11 | 648.030029 |
Optional: fetch fresh data yourself¶
The cell below downloads live data with yfinance instead of reading the snapshot. It needs a network connection (and pip install yfinance), so it is not run by default -- change the flag to True if you want today's prices. Everything after this point works the same either way.
FETCH_LIVE_DATA = False # set to True to download fresh prices instead of the snapshot
if FETCH_LIVE_DATA:
import yfinance as yf
live = yf.Ticker("META").history(period="10y", interval="1d", auto_adjust=True)
live.index = live.index.tz_localize(None).normalize()
live.index.name = "Date"
df = live[["Close"]].rename(columns={"Close": "Adj. Close"})
print(f"downloaded {len(df)} trading days up to {df.index.max().date()}")
We only need the Adj. Close column for our predictions.
df = df[['Adj. Close']]
print(df.tail())
Adj. Close Date 2026-09-04 616.770020 2026-09-08 613.479980 2026-09-09 653.690002 2026-09-10 644.380005 2026-09-11 648.030029
Now, let's set up our forecasting. We want to predict 30 days into the future, so we'll set a variable forecast_out equal to that. Then, we need to create a new column in our dataframe which serves as our label, which, in machine learning, is known as our output. To fill our output data with data to be trained upon, we will set our prediction column equal to our Adj. Close column, but shifted 30 units up.
forecast_out = int(30) # predicting 30 days into the future
df['Prediction'] = df[['Adj. Close']].shift(-forecast_out) # label column, data shifted 30 units up
You can see the new dataframe by printing it:
print(df.tail())
Adj. Close Prediction Date 2026-09-04 616.770020 NaN 2026-09-08 613.479980 NaN 2026-09-09 653.690002 NaN 2026-09-10 644.380005 NaN 2026-09-11 648.030029 NaN
Defining features & labels¶
Our X will be an array consisting of our Adj. Close values, and so we want to drop the Prediction column. We also want to scale our input values. Scaling our features allows us to normalize the data.
X = np.array(df.drop(columns=['Prediction']))
X = preprocessing.scale(X)
Now, if you printed the dataframe after we created the Prediction column, you saw that for the last 30 days there were NaNs, i.e. no label data. We'll set a new input variable to these days and remove them from the X array.
X_forecast = X[-forecast_out:] # set X_forecast equal to the last 30 days
X = X[:-forecast_out] # remove the last 30 from X
To define our y, or output, we will set it equal to our array of the Prediction values and remove the last 30 days where we don't have any pricing data.
y = np.array(df['Prediction'])
y = y[:-forecast_out]
Linear regression¶
Finally, prediction time! First, we'll want to split our testing and training data sets, and set our test_size equal to 20% of the data. The training set contains our known outputs, or prices, that our model learns on, and our test dataset is there to test our model's predictions against what it learned from the training set.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
Now we can initiate our Linear Regression model and fit it with the training data. After training, to test the accuracy of the model, we "score" it using the testing data. We get an $r^2$ (coefficient of determination) reading based on how far the predicted price was from the actual price in the test data set.
# Training
clf = LinearRegression()
clf.fit(X_train, y_train)
# Testing
confidence = clf.score(X_test, y_test)
print("confidence: ", confidence)
confidence: 0.9494217584326416
Lastly, we can predict our X_forecast values -- the 30 trading days that follow the end of our data:
forecast_prediction = clf.predict(X_forecast)
print(forecast_prediction)
[559.58009951 592.70890893 590.4364384 591.25652591 592.37301034 594.54664172 597.33290965 601.48267195 581.45516711 597.38229958 592.32356112 571.69336827 546.69607388 549.02788387 548.83026485 552.85157755 561.86246027 572.76046375 578.77762857 573.7978895 580.63514085 575.02310431 581.14887798 595.28766858 612.90436405 618.92152886 615.6708515 655.39979355 646.20118303 649.80753748]
What's next?¶
Try to plot your data using matplotlib, next to the realised prices. Make your predictions more advanced by including more features -- volume, moving averages, the prices of related stocks.
And keep the result in perspective: a high $r^2$ here mostly says that tomorrow's price looks a lot like today's, not that we can beat the market.