Signals·Machine Learning Models·Advanced

Prophet Price Forecast

Apply Meta Prophet for financial time series forecasting, decomposing price data into trend, seasonality, and holiday components with full uncertainty intervals around point predictions for risk-aware trading decisions.

machine-learningtrading-signals

Prophet Price Forecasting

Prophet is an open-source forecasting tool developed by Facebook (now Meta). It is designed to make forecasting at scale easier, especially for business time series with strong seasonal effects and holidays. It works best with time series data that have hourly, daily, or weekly observations, and that exhibit several seasons of historical data.

Why Prophet for Price Forecasting?

Price series data, especially in financial markets or retail, often exhibit clear trends, daily, weekly, or yearly seasonality, and are influenced by external events (like holidays or promotions). Prophet is well-suited for such data due to its:

  • Automatic handling of seasonality: It can detect and model multiple types of seasonality (e.g., daily, weekly, yearly).
  • Robustness to outliers: It is designed to be robust to missing data and shifts in the trend.
  • Holiday and custom event support: It allows users to define custom events that can impact the time series.
  • Interpretable parameters: Its components (trend, seasonality, holidays) are easy to understand.

This notebook will guide you through the process of using Prophet for price forecasting, from data preparation to model interpretation.

[3]
# Install necessary libraries
!pip install prophet pandas matplotlib numpy
Requirement already satisfied: prophet in /usr/local/lib/python3.12/dist-packages (1.3.0)
Requirement already satisfied: pandas in /usr/local/lib/python3.12/dist-packages (2.2.2)
Requirement already satisfied: matplotlib in /usr/local/lib/python3.12/dist-packages (3.10.0)
Requirement already satisfied: numpy in /usr/local/lib/python3.12/dist-packages (2.0.2)
Requirement already satisfied: cmdstanpy>=1.0.4 in /usr/local/lib/python3.12/dist-packages (from prophet) (1.3.0)
Requirement already satisfied: holidays<1,>=0.25 in /usr/local/lib/python3.12/dist-packages (from prophet) (0.98)
Requirement already satisfied: tqdm>=4.36.1 in /usr/local/lib/python3.12/dist-packages (from prophet) (4.67.3)
Requirement already satisfied: importlib_resources in /usr/local/lib/python3.12/dist-packages (from prophet) (7.1.0)
Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas) (2.9.0.post0)
Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas) (2025.2)
Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas) (2026.2)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (1.3.3)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (4.63.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (1.5.0)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (26.2)
Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (11.3.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (3.3.2)
Requirement already satisfied: stanio<2.0.0,>=0.4.0 in /usr/local/lib/python3.12/dist-packages (from cmdstanpy>=1.0.4->prophet) (0.5.1)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)
[4]
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from prophet import Prophet

# Suppress warnings for cleaner output
import warnings
warnings.filterwarnings('ignore')

Understanding Prophet's Model Components

Prophet decomposes a time series into three main components:

$$y(t) = g(t) + s(t) + h(t) + \epsilon_t$$

Where:

  • $g(t)$: Trend component, which models non-periodic changes in the time series.
  • $s(t)$: Seasonality component, which models periodic changes (e.g., weekly, yearly).
  • $h(t)$: Holiday component, which models the impact of specified holidays or irregular events.
  • $\epsilon_t$: Error term, representing any idiosyncratic changes not accommodated by the model.

Data Preparation for Prophet

Prophet requires the input DataFrame to have two specific columns:

  • ds: (datestamp) column, which should be of a format parseable by Pandas (e.g., YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).
  • y: (numeric) column, which represents the measurement we want to forecast (e.g., price).
[5]
import pandas as pd
import numpy as np

def generate_mock_price_data(start_date='2020-01-01', periods=730, initial_price=100, trend_strength=0.05, seasonality_amplitude_base=5, volatility=2, spike_frequency=0.02, spike_magnitude=10):
    """
    Generates more realistic mock daily price data with trend, seasonality, noise, and occasional spikes.

    Inputs:
        start_date (str): Start date for the time series.
        periods (int): Number of days to generate data for.
        initial_price (float): Starting price.
        trend_strength (float): Overall strength of the linear trend component.
        seasonality_amplitude_base (float): Base amplitude for seasonal components.
        volatility (float): Standard deviation for daily random price changes.
        spike_frequency (float): Probability of a random spike/drop occurring on any given day.
        spike_magnitude (float): Maximum magnitude of a random spike/drop.

    Outputs:
        pd.DataFrame: A DataFrame with 'ds' and 'y' columns.
    """
    dates = pd.date_range(start=start_date, periods=periods, freq='D')
    time_index = np.arange(periods)

    # 1. Base Trend: A general upward trend, but not perfectly linear
    # Using a slightly curved trend to make it less obvious
    base_trend = initial_price + trend_strength * time_index + 5 * np.sin(time_index / 100)

    # 2. Weekly Seasonality: More complex than a single sine wave
    weekly_seasonality = (
        seasonality_amplitude_base * np.sin(2 * np.pi * (time_index % 7) / 7) +
        (seasonality_amplitude_base / 2) * np.cos(2 * np.pi * (time_index % 7) / 7 + np.pi/4)
    )

    # 3. Yearly Seasonality: More complex
    yearly_seasonality = (
        seasonality_amplitude_base * 1.5 * np.sin(2 * np.pi * time_index / 365) +
        (seasonality_amplitude_base / 2) * np.cos(2 * np.pi * time_index / (365/2) + np.pi/2)
    )

    # 4. Random Walk / Daily Price Changes (autocorrelation)
    # Each day's price change is a small random step
    daily_changes = np.random.normal(0, volatility, periods).cumsum()

    # 5. Occasional Spikes/Drops
    spikes = np.zeros(periods)
    for i in range(periods):
        if np.random.rand() < spike_frequency:
            # Randomly choose between a spike or a drop
            spike_direction = 1 if np.random.rand() > 0.5 else -1
            spikes[i] = spike_direction * np.random.uniform(spike_magnitude / 2, spike_magnitude)

    # Combine components
    prices = base_trend + weekly_seasonality + yearly_seasonality + daily_changes + spikes
    prices = np.maximum(5, prices) # Ensure prices are non-negative and have a floor

    df = pd.DataFrame({
        'ds': dates,
        'y': prices
    })
    return df

# Generate sample price data for 2 years with revised parameters
df_price = generate_mock_price_data(periods=730, initial_price=100, trend_strength=0.08, seasonality_amplitude_base=7, volatility=1.5, spike_frequency=0.03, spike_magnitude=15)
display(df_price.head())
display(df_price.tail())
ds y
0 2020-01-01 102.814512
1 2020-01-02 104.439765
2 2020-01-03 102.895106
3 2020-01-04 100.444020
4 2020-01-05 101.073664
ds y
725 2021-12-26 145.151941
726 2021-12-27 144.788752
727 2021-12-28 149.430002
728 2021-12-29 157.051613
729 2021-12-30 158.802334
Error: Runtime no longer has a reference to this dataframe, please re-run this cell and try again.

Initializing and Fitting the Prophet Model

Once the data is prepared, initializing and fitting the Prophet model is straightforward. You can customize various parameters during initialization, but for a basic forecast, the default settings often work well.

  • Prophet(): Initializes the model. You can specify parameters like seasonality_mode, yearly_seasonality, weekly_seasonality, daily_seasonality, holidays, etc.
  • fit(df): Trains the model using your historical data. It learns the underlying trend, seasonality, and holiday effects from the provided ds and y columns.
[6]
# Initialize Prophet model
# We explicitly set yearly and weekly seasonality to True as our data has these components.
# For financial data, 'multiplicative' seasonality might sometimes be more appropriate if the seasonal effect scales with the trend.
# Here, we'll start with 'additive' which is the default.
m = Prophet(yearly_seasonality=True, weekly_seasonality=True, daily_seasonality=False)

# Fit the model to the historical data
m.fit(df_price)

print("Prophet model fitted successfully!")
Prophet model fitted successfully!

Making Future Predictions

After fitting the model, you can make predictions for future time points.

  • make_future_dataframe(periods=int, freq=str): Creates a DataFrame with future dates (and historical dates used for training). periods specifies the number of future time steps, and freq specifies the frequency (e.g., 'D' for daily, 'H' for hourly).
  • predict(future_df): Generates a forecast DataFrame containing predictions (yhat), along with lower (yhat_lower) and upper (yhat_upper) bounds for the predictions.
[7]
# Create a DataFrame with future dates for forecasting
# Let's forecast for the next 365 days (1 year)
future = m.make_future_dataframe(periods=365)

# Make predictions
forecast = m.predict(future)

print("Forecast DataFrame head:")
display(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head())

print("\nForecast DataFrame tail (showing future predictions):")
display(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
Forecast DataFrame head:
ds yhat yhat_lower yhat_upper
0 2020-01-01 107.548795 103.194590 111.335827
1 2020-01-02 110.001342 105.927107 113.746103
2 2020-01-03 109.155178 105.309395 113.247405
3 2020-01-04 105.522851 101.513271 109.614359
4 2020-01-05 102.291513 98.105198 106.105319

Forecast DataFrame tail (showing future predictions):
ds yhat yhat_lower yhat_upper
1090 2022-12-26 216.285730 177.180685 253.835898
1091 2022-12-27 219.439381 179.985065 256.979869
1092 2022-12-28 223.710860 184.703497 260.976656
1093 2022-12-29 225.989310 187.110168 263.941213
1094 2022-12-30 224.973425 185.514845 263.081453
Error: Runtime no longer has a reference to this dataframe, please re-run this cell and try again.

Visualizing the Forecast

Prophet provides convenient plotting functions to visualize the forecast and its components.

1. Overall Forecast Plot

This plot shows the historical data (black dots), the model's fitted line (blue line), and the predicted future values with uncertainty intervals (light blue shaded area). This helps in visually assessing the model's performance and the general trend of the forecast.

[8]
# Plot the overall forecast
fig1 = m.plot(forecast, figsize=(10, 6))
plt.title('Prophet Price Forecast (Historical + Predicted)')
plt.xlabel('Date')
plt.ylabel('Price')
plt.grid(True)
plt.show()

# Interpretation:
# The black dots represent the actual historical price data.
# The blue line represents the Prophet model's fit to the historical data and its forecast into the future.
# The light blue shaded area indicates the uncertainty interval (yhat_lower to yhat_upper) for the predictions.
# This visualization helps to see how well the model captures the historical trend and seasonality, and where it predicts the price will go.
cell output

2. Forecast Components Plot

This plot breaks down the forecast into its individual components: trend, yearly seasonality, and weekly seasonality. This is crucial for understanding the underlying drivers of the price movements and for gaining insights into the seasonal patterns.

  • Trend: Shows the overall long-term direction of the price.
  • Yearly Seasonality: Reveals how the price typically behaves over the course of a year.
  • Weekly Seasonality: Shows typical price patterns within a week (e.g., higher on certain days).
[9]
# Plot the forecast components
fig2 = m.plot_components(forecast, figsize=(10, 8))
plt.suptitle('Prophet Forecast Components', y=1.02) # Add a main title for all subplots
plt.tight_layout(rect=[0, 0.03, 1, 0.98]) # Adjust layout to prevent title overlap
plt.show()

# Interpretation:
# The top plot shows the overall trend identified by Prophet, which appears to be increasing over time in our mock data.
# The middle plot illustrates the yearly seasonality. For our mock data, there's a clear sinusoidal pattern, representing price fluctuations over a year.
# The bottom plot displays the weekly seasonality. This indicates typical price behavior on each day of the week, e.g., potentially higher prices on weekends or specific weekdays.
cell output

Customizing Prophet (Advanced)

Prophet offers several ways to customize the model for better performance and more nuanced forecasts:

  • Adding Holidays and Special Events: You can define a DataFrame of custom events (e.g., public holidays, product launches, market closures) that can influence prices.
  • Custom Seasonality: Beyond yearly, weekly, and daily, you can add custom seasonalities (e.g., quarterly, hourly) if your data supports it.
  • Changepoint Detection: Prophet automatically detects changepoints in the trend, but you can manually specify potential changepoint dates or adjust the changepoint_prior_scale parameter to control the flexibility of the trend.
  • Seasonality Mode: Change seasonality_mode from 'additive' to 'multiplicative' if the seasonal effect grows with the trend (common in financial time series).

For example, to add custom holidays:

[10]
# Example: Adding custom holidays
# Create a DataFrame of holidays (e.g., a 'Black Friday' effect)
holidays = pd.DataFrame({
    'holiday': 'black_friday',
    'ds': pd.to_datetime(['2020-11-27', '2021-11-26']),
    'lower_window': 0,
    'upper_window': 1,
})

# Initialize Prophet with holidays
m_holidays = Prophet(holidays=holidays, yearly_seasonality=True, weekly_seasonality=True)
m_holidays.fit(df_price)
future_holidays = m_holidays.make_future_dataframe(periods=365)
forecast_holidays = m_holidays.predict(future_holidays)

print("Forecast with Black Friday holiday effect:")
display(forecast_holidays[['ds', 'yhat', 'black_friday']].tail())

# You can also plot the holiday components:
fig_holidays = m_holidays.plot_components(forecast_holidays)
plt.show()
INFO:prophet:Disabling daily seasonality. Run prophet with daily_seasonality=True to override this.
Forecast with Black Friday holiday effect:
ds yhat black_friday
1090 2022-12-26 216.742002 0.0
1091 2022-12-27 219.913530 0.0
1092 2022-12-28 224.206290 0.0
1093 2022-12-29 226.504415 0.0
1094 2022-12-30 225.488938 0.0
cell output

Conclusion

Prophet is a powerful and user-friendly tool for forecasting time series data, particularly well-suited for price forecasting due to its ability to handle trends, various seasonalities, and custom events. By understanding its components and how to prepare data, you can generate robust and interpretable price forecasts. Remember to always evaluate your model's performance and consider domain-specific knowledge to fine-tune your predictions.