Forecast Stock Prices with Prophet

Build a Python tool that forecasts 90 days of AAPL stock prices.

Introduction

30 Second Summary

Everyone wants to know where stock prices are heading next. Predicting the future is impossible, but data science gives us tools to spot trends and patterns hidden inside years of historical data.

In this project, you will download real Apple (AAPL) stock data and train a time series forecasting model called Prophet. The model generates a 90-day price prediction complete with visual charts and a printed forecast summary.

What You'll Build

A Python forecasting tool that pulls 5 years of historical AAPL data from Yahoo Finance using the yfinance library, trains a Prophet model with pandas, and outputs a 90-day price forecast with matplotlib charts.

By the end of this project, you'll have:

  • A Python script that fetches live stock data from Yahoo Finance and trains a Prophet forecasting model.
  • Visual charts showing AAPL's 5-year price history and a 90-day forecast with confidence intervals.
  • A trend and seasonality decomposition chart revealing the underlying patterns Prophet found in the stock data.
  • Secret Mission: added US holiday effects to the model and compared forecast accuracy with and without holidays.

Are there any prerequisites?

You need Python 3.10 or higher installed on your computer. No prior experience with data science or stock markets is required.

Set Up Your Environment

Before you can forecast stock prices, you need the right tools installed. This step gets your Python environment ready with all the libraries the project depends on.

You'll install four libraries: yfinance for downloading stock data, Prophet for time series forecasting, pandas for data manipulation, and matplotlib for charting.

In this step, get ready to:
  • Install Python libraries for data fetching, forecasting, and visualization.
  • Verify everything works by running a test script.
Create your project folder and install libraries

Every command in this project runs in your terminal.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir stock-forecast
cd stock-forecast
  • Confirm the folder exists by running:
ls

The output should be empty since your stock-forecast folder is brand new.

  • Install all the required Python libraries by running:
pip install yfinance==1.3.0 prophet==1.3.0 pandas matplotlib

What does this command install?

  • yfinance downloads stock market data directly from Yahoo Finance. No API key needed.
  • prophet is Meta's open-source time series forecasting library. It handles trends and seasonality automatically.
  • pandas organizes your stock data into structured tables called DataFrames.
  • matplotlib creates the charts and visualizations for your forecast.

This installation may take a few minutes, especially for Prophet which has several dependencies. You should see output ending with Successfully installed followed by a list of package names.

Installation failing?

  • If pip install prophet fails, try upgrading pip first by running this command:
python -m pip install --upgrade pip
  • Then retry the full install command from above.
  • Prophet requires Python 3.10 or higher. Check your version with python --version in your terminal.
  • On Windows, make sure you are using PowerShell instead of Command Prompt for the best compatibility.

Help me troubleshoot my Prophet installation

Verify the installation

A quick test script confirms that all four libraries import correctly before you start building the real forecasting code.

  • Create a new file called stock_forecast.py inside your stock-forecast folder using any text editor.
  • Add this verification code to stock_forecast.py:
import yfinance as yf
import pandas as pd
from prophet import Prophet
import matplotlib

# Print version numbers to confirm each library loaded
print("yfinance version:", yf.__version__)
print("pandas version:", pd.__version__)
print("All libraries installed successfully!")

What does this code do?

  • Each import line tries to load one of the libraries you just installed. If any import fails, Python raises an error immediately.
  • The print statements confirm which versions are loaded so you can verify the install worked.
  • Save stock_forecast.py.
  • Run the script by executing this command in your terminal:
python stock_forecast.py

You should see version numbers for yfinance and pandas, followed by All libraries installed successfully!.

Seeing a message about cmdstan?

Prophet uses a tool called cmdstan under the hood. On its first run, it may download or compile cmdstan binaries automatically. This can take 1-2 minutes and prints extra output. This is completely normal.

Seeing an import error?

  • A ModuleNotFoundError means the library did not install correctly. Re-run the pip install command from the previous substep.
  • Make sure you are running the script from inside the stock-forecast folder where the file lives.
  • If you have multiple Python versions installed, try using python3 instead of python in the run command.

Help me fix my import errors

Your environment is ready to go. Next up, you'll download real Apple stock data and explore what 5 years of price history looks like.

Download and Explore Stock Data

Your environment is set up with all the libraries you need. Now it's time to pull in real financial data and see what it looks like.

You'll use yfinance to download 5 years of Apple (AAPL) stock prices directly from Yahoo Finance. Then you'll create a chart with matplotlib to visualize how the price has moved over time.

In this step, get ready to:
  • Download 5 years of AAPL historical price data from Yahoo Finance.
  • Visualize the closing price trend with a matplotlib chart.
Download 5 years of Apple stock data

The yfinance library connects to Yahoo Finance and returns stock data as a pandas DataFrame. Each row represents one trading day, with columns for the opening price, high, low, closing price, and trading volume.

  • Replace the entire contents of stock_forecast.py with the following code:
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from prophet import Prophet

# Download 5 years of AAPL stock data
ticker = yf.Ticker("AAPL")
df = ticker.history(period="5y")

print(f"Downloaded {len(df)} rows of AAPL stock data")
print(f"Date range: {df.index.min().date()} to {df.index.max().date()}")
print(f"\nFirst 5 rows:")
print(df.head())
print(f"\nPrice statistics:")
print(df['Close'].describe())

What does this code do?

  • yf.Ticker("AAPL") creates a connection to Apple's stock data on Yahoo Finance.
  • ticker.history(period="5y") downloads 5 years of daily price data into a pandas DataFrame.
  • The print statements show the data shape, date range, first rows, and basic price statistics so you can verify the download worked.
  • The imports at the top bring in all four libraries you'll use throughout this project. Prophet isn't used yet, but importing it now keeps all imports in one place.
  • Save stock_forecast.py.
  • Run the script by running this command:
python stock_forecast.py

You should see roughly 1,250 rows of data with a date range spanning 5 years. The output includes the first 5 rows showing columns like Open, High, Low, Close, and Volume, followed by price statistics like the mean and standard deviation.

Seeing a download error?

  • Make sure you have an active internet connection. yfinance pulls data live from Yahoo Finance.
  • If you see a No data found error, Yahoo Finance may be temporarily unavailable. Wait a minute and try again.
  • If the downloaded row count is 0, double check that the ticker symbol "AAPL" is spelled exactly as shown in the code.
  • Help me debug my yfinance download error.
Visualize the historical prices

Raw numbers are hard to read on their own. A price chart makes it much easier to spot trends and patterns before you build a forecast model.

  • Add this code below your existing code in stock_forecast.py:
# Visualize historical prices
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['Close'], color='blue', linewidth=1)
plt.title('AAPL Stock Price - Last 5 Years')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('stock_history.png', dpi=150)
plt.show()
print("Saved chart to stock_history.png")

What does this code do?

  • plt.figure(figsize=(12, 6)) creates a wide chart canvas (12 inches by 6 inches).
  • plt.plot(df.index, df['Close']) plots the closing price for each trading day as a blue line.
  • plt.savefig('stock_history.png', dpi=150) saves the chart as an image file in your project folder so you have a copy even after closing the window.
  • plt.show() opens the chart in an interactive popup window.
  • Save stock_forecast.py and run the script again.

A chart window opens showing AAPL's price history over the last 5 years. Close the chart window to let the script finish running.

  • Check your stock-forecast folder. You should see a new file called stock_history.png.

Chart window not showing up?

  • Some environments run matplotlib in non-interactive mode. If no window appears, open stock_history.png directly from your project folder to see the chart.
  • If you see a TclError or display error, you may be running in a headless environment like a remote server. The chart is still saved as stock_history.png.
  • Make sure you saved stock_forecast.py before running. Unsaved changes won't execute.
  • Why isn't my matplotlib chart window appearing?

Awesome, I've got everything!

Double check you've saved stock_forecast.py and you're ready to move on.

I'd like to double check the full code

Here is the full stock_forecast.py file up to this point. Compare it against yours to catch any differences.

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from prophet import Prophet

# Download 5 years of AAPL stock data
ticker = yf.Ticker("AAPL")
df = ticker.history(period="5y")

print(f"Downloaded {len(df)} rows of AAPL stock data")
print(f"Date range: {df.index.min().date()} to {df.index.max().date()}")
print(f"\nFirst 5 rows:")
print(df.head())
print(f"\nPrice statistics:")
print(df['Close'].describe())

# Visualize historical prices
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['Close'], color='blue', linewidth=1)
plt.title('AAPL Stock Price - Last 5 Years')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('stock_history.png', dpi=150)
plt.show()
print("Saved chart to stock_history.png")

You have real stock data loaded and visualized. Next up, you'll prepare this data for Prophet and train your first forecasting model.

Prepare Data and Train the Forecast Model

You now have 5 years of AAPL stock data downloaded and a historical price chart saved to your project folder.

Prophet can't work with raw stock data directly. It expects a very specific two-column format. In this step, you'll reshape your data to match that format, train a forecasting model, and generate a 90-day price prediction.

In this step, get ready to:
  • Transform stock data into Prophet's required two-column format.
  • Train a Prophet forecasting model on 5 years of historical prices.
  • Generate a 90-day price forecast with confidence intervals.
Prepare the data for Prophet

Prophet requires a DataFrame with exactly two columns: ds (the date) and y (the value to forecast). Your stock data currently has different column names and timezone-aware dates, so it needs to be reshaped.

  • In stock_forecast.py, add this code below your chart code to transform the stock data into Prophet's format:
# Prepare data for Prophet
prophet_df = df.reset_index()[['Date', 'Close']].copy()
prophet_df.columns = ['ds', 'y']
prophet_df['ds'] = pd.to_datetime(prophet_df['ds']).dt.tz_localize(None)

print(f"\nPrepared data for Prophet:")
print(prophet_df.head())
print(f"Shape: {prophet_df.shape}")

What does this code do?

  • reset_index() converts the date index into a regular column so you can rename it.
  • The column rename from Date and Close to ds and y gives Prophet the exact column names it expects.
  • tz_localize(None) strips timezone information from the dates. yfinance returns timezone-aware timestamps, but Prophet expects timezone-naive ones.
  • Save stock_forecast.py. You'll run the full script after adding the model code next.
Train the Prophet model and generate a 90-day forecast

With your data in the right shape, you can now create a Prophet model, fit it on the historical prices, and ask it to predict the next 90 days.

  • Add this code below your data preparation block in stock_forecast.py:
# Train the Prophet model
model = Prophet(daily_seasonality=False)
model.fit(prophet_df)

# Generate 90-day forecast
future = model.make_future_dataframe(periods=90)
forecast = model.predict(future)

print(f"\nForecast for the next 90 days:")
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(10))

What does this code do?

  • daily_seasonality=False tells Prophet not to look for within-day patterns. Your data has one price per trading day, so daily seasonality would just fit noise.
  • model.fit(prophet_df) trains the model on your 5 years of historical prices. This is where Prophet learns the trend and seasonal patterns.
  • make_future_dataframe(periods=90) creates a new DataFrame extending 90 days past the last known date.
  • The forecast output includes three key columns: yhat (predicted price), yhat_lower (low estimate), and yhat_upper (high estimate).
  • Save stock_forecast.py.
Run the script and check the forecast output
  • Run your updated script by executing this command:
python stock_forecast.py

The model fitting takes a few seconds. After the historical chart opens and closes, you should see the last 10 rows of the forecast printed in your terminal with columns for ds, yhat, yhat_lower, and yhat_upper.

What should I see?

You should see a table of 10 rows with future dates and predicted prices. Each row has a predicted value (yhat) plus a lower and upper bound representing the confidence interval. The wider the gap between yhat_lower and yhat_upper, the less certain the model is about that prediction.

Seeing an error or unexpected output?

  • If Prophet prints verbose messages about cmdstan on the first run, that is normal. It downloads and compiles model binaries the first time. This can take 1-2 minutes.
  • If you see a KeyError: 'Date', make sure you did not remove the data download code from Step 2. The df variable must exist before the preparation code runs.
  • If you see TypeError related to timestamps, double check you included the .dt.tz_localize(None) line in your data preparation block.
  • Still stuck? Help me debug my Prophet forecast script.

Awesome, I've got everything!

Great. Double check you've saved stock_forecast.py before moving on.

I'd like to double check the full code

Here is what your full stock_forecast.py should look like at this point.

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from prophet import Prophet

# Download 5 years of AAPL stock data
ticker = yf.Ticker("AAPL")
df = ticker.history(period="5y")

print(f"Downloaded {len(df)} rows of AAPL stock data")
print(f"Date range: {df.index.min().date()} to {df.index.max().date()}")
print(f"\nFirst 5 rows:")
print(df.head())
print(f"\nPrice statistics:")
print(df['Close'].describe())

# Visualize historical prices
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['Close'], color='blue', linewidth=1)
plt.title('AAPL Stock Price - Last 5 Years')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('stock_history.png', dpi=150)
plt.show()
print("Saved chart to stock_history.png")

# Prepare data for Prophet
prophet_df = df.reset_index()[['Date', 'Close']].copy()
prophet_df.columns = ['ds', 'y']
prophet_df['ds'] = pd.to_datetime(prophet_df['ds']).dt.tz_localize(None)

print(f"\nPrepared data for Prophet:")
print(prophet_df.head())
print(f"Shape: {prophet_df.shape}")

# Train the Prophet model
model = Prophet(daily_seasonality=False)
model.fit(prophet_df)

# Generate 90-day forecast
future = model.make_future_dataframe(periods=90)
forecast = model.predict(future)

print(f"\nForecast for the next 90 days:")
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(10))

Your model is trained and you have 90 days of predicted prices sitting in a DataFrame. Next up, you'll turn those numbers into visual forecast charts and a printed summary.

Visualize and Interpret Your Forecast

Your Prophet model is trained and the forecast numbers are ready. Now it's time to turn those numbers into something you can actually see and interpret.

You'll generate a forecast chart, a trend and seasonality decomposition, and a printed summary comparing actual prices to predictions.

In this step, get ready to:
  • Plot the forecast chart with confidence intervals.
  • Generate a trend and seasonality decomposition chart.
  • Print a forecast summary comparing actual vs. predicted prices.
Plot the full forecast

Prophet has a built-in plotting method that overlays your historical data with the forecast. The chart shows actual prices as black dots and the predicted trend as a blue line with a shaded confidence interval.

  • Add this code below your existing code in stock_forecast.py to generate the forecast chart:
# Plot the forecast
fig1 = model.plot(forecast)
plt.title('AAPL Stock Price Forecast - Next 90 Days')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.tight_layout()
plt.savefig('stock_forecast.png', dpi=150)
plt.show()
print("Saved forecast chart to stock_forecast.png")

What does this code do?

  • model.plot(forecast) uses Prophet's built-in charting to plot historical data as black dots and the prediction as a blue line with a shaded confidence band.
  • plt.savefig() saves the chart as a PNG file so you can reference it later without rerunning the script.
  • plt.show() opens the chart in an interactive window for immediate viewing.
  • Save stock_forecast.py. You'll run the script after adding the remaining visualizations.
Plot the trend and seasonality components

Prophet can decompose your forecast into its individual building blocks. The components chart shows the overall trend (long-term direction of the stock) and weekly seasonality (repeating patterns within each week).

  • Add this code below the forecast chart code in stock_forecast.py:
# Plot trend and seasonality components
fig2 = model.plot_components(forecast)
plt.tight_layout()
plt.savefig('forecast_components.png', dpi=150)
plt.show()
print("Saved components chart to forecast_components.png")

What does this code do?

  • model.plot_components(forecast) generates separate panels for each component Prophet learned: trend and weekly seasonality.
  • The trend panel shows whether the stock has been generally rising or falling over the 5-year period.
  • The weekly seasonality panel reveals which days of the week tend to have higher or lower prices.
  • Save stock_forecast.py. One more piece to add before running the full script.
Print a forecast summary

Charts are great for visual interpretation. A printed summary makes it easy to compare specific numbers side by side.

  • Add this final block of code to stock_forecast.py:
# Forecast summary
last_actual = prophet_df['y'].iloc[-1]
future_forecast = forecast[forecast['ds'] > prophet_df['ds'].max()]
last_forecast = future_forecast.iloc[-1]

print(f"\n{'='*50}")
print(f"FORECAST SUMMARY")
print(f"{'='*50}")
print(f"Last actual closing price: ${last_actual:.2f}")
print(f"Forecast for {last_forecast['ds'].date()}:")
print(f"  Predicted: ${last_forecast['yhat']:.2f}")
print(f"  Low estimate: ${last_forecast['yhat_lower']:.2f}")
print(f"  High estimate: ${last_forecast['yhat_upper']:.2f}")

What does this code do?

  • prophet_df['y'].iloc[-1] grabs the most recent actual closing price from your historical data.
  • The filter forecast['ds'] > prophet_df['ds'].max() isolates only the future predictions, excluding the historical period.
  • last_forecast is the final row of the forecast, representing the price prediction 90 days out.
  • The summary prints the predicted price along with the low and high estimates from the confidence interval.
  • Save stock_forecast.py.
  • Run the full script by running this command:
python stock_forecast.py

Three chart windows open one after another. Close each chart window to see the next one.

After the charts, your terminal displays the forecast summary with the predicted price, low estimate, and high estimate. The charts are also saved as stock_history.png, stock_forecast.png, and forecast_components.png in your stock-forecast folder.

Charts not appearing?

  • If chart windows don't open, the charts are still saved as PNG files in your project folder. Open them directly to see your results.
  • If you see NameError: name 'model' is not defined, make sure you haven't removed the model training code from earlier in the file.
  • If charts appear blank or malformed, check that matplotlib is installed by running pip install matplotlib and rerunning the script.
  • My stock forecast script is not displaying charts or is throwing an error when I try to visualize.

What should I see in the forecast chart?

The forecast chart shows black dots for every historical closing price over the last 5 years. A blue line extends past the last data point, showing the predicted price for the next 90 days. The light blue shaded area around the prediction is the confidence interval.

The wider the shaded area, the less certain the model is about that prediction. You'll notice it gets wider the further into the future it goes.

Awesome, I've got everything!

Double check you've saved stock_forecast.py and that all three charts display when you run the script.

I'd like to double check the full code

Here is the complete stock_forecast.py file. Compare your code against this:

import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
from prophet import Prophet

# Download 5 years of AAPL stock data
ticker = yf.Ticker("AAPL")
df = ticker.history(period="5y")

print(f"Downloaded {len(df)} rows of AAPL stock data")
print(f"Date range: {df.index.min().date()} to {df.index.max().date()}")
print(f"\nFirst 5 rows:")
print(df.head())
print(f"\nPrice statistics:")
print(df['Close'].describe())

# Visualize historical prices
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['Close'], color='blue', linewidth=1)
plt.title('AAPL Stock Price - Last 5 Years')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('stock_history.png', dpi=150)
plt.show()
print("Saved chart to stock_history.png")

# Prepare data for Prophet
prophet_df = df.reset_index()[['Date', 'Close']].copy()
prophet_df.columns = ['ds', 'y']
prophet_df['ds'] = pd.to_datetime(prophet_df['ds']).dt.tz_localize(None)

print(f"\nPrepared data for Prophet:")
print(prophet_df.head())
print(f"Shape: {prophet_df.shape}")

# Train the Prophet model
model = Prophet(daily_seasonality=False)
model.fit(prophet_df)

# Generate 90-day forecast
future = model.make_future_dataframe(periods=90)
forecast = model.predict(future)

print(f"\nForecast for the next 90 days:")
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(10))

# Plot the forecast
fig1 = model.plot(forecast)
plt.title('AAPL Stock Price Forecast - Next 90 Days')
plt.xlabel('Date')
plt.ylabel('Price (USD)')
plt.tight_layout()
plt.savefig('stock_forecast.png', dpi=150)
plt.show()
print("Saved forecast chart to stock_forecast.png")

# Plot trend and seasonality components
fig2 = model.plot_components(forecast)
plt.tight_layout()
plt.savefig('forecast_components.png', dpi=150)
plt.show()
print("Saved components chart to forecast_components.png")

# Forecast summary
last_actual = prophet_df['y'].iloc[-1]
future_forecast = forecast[forecast['ds'] > prophet_df['ds'].max()]
last_forecast = future_forecast.iloc[-1]

print(f"\n{'='*50}")
print(f"FORECAST SUMMARY")
print(f"{'='*50}")
print(f"Last actual closing price: ${last_actual:.2f}")
print(f"Forecast for {last_forecast['ds'].date()}:")
print(f"  Predicted: ${last_forecast['yhat']:.2f}")
print(f"  Low estimate: ${last_forecast['yhat_lower']:.2f}")
print(f"  High estimate: ${last_forecast['yhat_upper']:.2f}")

Secret mission

Add Holiday Effects to Improve Accuracy

Stock markets are closed on US holidays like Independence Day and Thanksgiving. These closures create patterns in the data that your current model completely ignores. Your mission is to add holiday awareness to Prophet and measure whether it actually improves the forecast.

Before you go

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally with free data from Yahoo Finance, so there are no ongoing costs.

Resources you used:

  • Project folder stock-forecast with your Python script.
  • Generated chart files: stock_history.png, stock_forecast.png, forecast_components.png.
  • Python packages installed via pip: yfinance, prophet, pandas, matplotlib.

Keep everything running

No action needed. Choose this if you want to keep experimenting with your forecast script, try different tickers, or extend the project further.

  • Your project folder and generated charts stay where they are.
  • You can re-run the script anytime to get updated stock data and a fresh forecast.

Pause - I'll come back to this later

Since this project is local-only, there is nothing to pause. Your files will remain on disk until you delete them.

  • Keep the stock-forecast folder intact so you can return later.
  • The installed Python packages stay available in your environment. They take up disk space but have no running processes.

Delete - I don't want to use this again

Remove all project resources and start fresh.

  • Delete the stock-forecast folder and all files inside it.
  • Optionally uninstall the Python packages by running:
pip uninstall yfinance prophet pandas matplotlib -y
  • Note that uninstalling pandas and matplotlib may affect other Python projects that depend on them. Only uninstall if you are sure no other projects need these packages.

Mission accomplished

That's a Wrap!

Congratulations! You just built a stock price forecasting tool from scratch using Python, yfinance, and Prophet.

You've learned how to:

  • Pull real stock market data from Yahoo Finance and visualize 5 years of AAPL price history with pandas and matplotlib.
  • Train a Prophet forecasting model and generate a 90-day price prediction with upper and lower confidence intervals.
  • Decompose your forecast into trend and seasonality components to interpret how the model understands price patterns over time.
  • Add US holiday effects to improve forecast accuracy and compare model performance with and without holidays.

Ready to quiz yourself?