FMP
Dec 30, 2025
Forecasting market trends is often approached as a modeling problem. In practice, it is primarily a data and system design problem. Many forecasting pipelines fail not because of the model itself, but because data preparation is fragile, feature logic is tightly coupled to training code, and swapping models requires rewriting large parts of the workflow. These issues make experimentation slow and results difficult to reproduce.
In this article, we build a market trend forecasting pipeline using Financial Modeling Prep (FMP) as the core data layer. FMP provides structured market datasets—such as historical prices, technical indicators, and market news—that are well suited for time-series workflows. Using these datasets allows the system to focus on organizing data and computation cleanly, rather than spending effort on sourcing, cleaning, and normalizing raw market inputs.
The pipeline is organized using a lightweight multi-agent structure to separate responsibilities across the major stages of forecasting. One agent handles market data collection from FMP, another prepares features for time-series modeling, and a final agent generates the forecast output. The agents execute in a simple linear sequence, keeping the system easy to inspect, modify, and debug.
For the forecasting component, the pipeline uses an LSTM-based model to capture temporal patterns in historical market data. The model is treated as a replaceable component rather than a fixed dependency. This separation makes it easier to experiment with alternative modeling approaches, iterate safely on features, and evolve the system without destabilizing the rest of the workflow.
By the end of this article, you will have a clear view of how to:
Taken together, these datasets are sufficient for a first-pass market trend forecaster: historical prices establish the primary signal, technical indicators add structured behavioral context, and news data supports post-forecast interpretation. This combination keeps the pipeline focused and manageable while covering the essential inputs needed for an end-to-end forecasting workflow.
The forecasting system is organized as a linear multi-agent pipeline. The goal of this structure is not orchestration complexity, but clear separation of responsibilities across the major stages of the workflow. Each agent owns a well-defined task and passes its output forward through a shared state object.
At a high level, the pipeline moves through three stages:
The agents execute sequentially in a fixed order. This keeps control flow explicit and avoids introducing routing logic that is unnecessary for a single-symbol forecasting task.
Using agents in this pipeline is a deliberate design choice to enforce clear boundaries between data access, feature preparation, and forecasting logic. Each agent represents a stable responsibility that maps directly to a stage in the forecasting workflow.
This separation allows each part of the system to evolve independently. New datasets can be added without changing feature logic, feature pipelines can be modified without retraining code, and forecasting models can be swapped or extended without touching data access. As a result, the pipeline remains easier to experiment with, safer to iterate on, and simpler to maintain as requirements change.
Routing between agents is intentionally simple. The system executes agents in a predefined order:
Data Agent → Feature Agent → Forecast Agent
Each agent receives a shared state object, updates it with its output, and passes it forward. This avoids hidden control flow and makes debugging straightforward.
While graph-based orchestration frameworks can be introduced later, a linear pipeline is sufficient for demonstrating how FMP data can be integrated into an agent-based forecasting workflow without additional abstraction layers.
Instead of separate state or routing files, agents communicate through a single shared state object that moves sequentially through the pipeline. This state holds the target symbol, retrieved market data, prepared feature matrices, and forecasting outputs.
Using a shared state makes data lineage explicit and keeps intermediate results easy to inspect at each stage of execution. When something breaks or produces unexpected output, developers can examine the state after any agent to identify where assumptions diverged or data changed shape. This also simplifies experimentation, since features, models, or datasets can be modified in isolation while preserving visibility into how those changes propagate through the workflow.
With this architecture in place, the next step is to examine which FMP datasets fit naturally into the pipeline and how they support market trend forecasting from an engineering perspective.
Before introducing models or agents, it is important to clarify which market datasets are required and how they map to a forecasting workflow. Trend forecasting relies on observing how prices evolve over time, augmenting those observations with derived signals, and optionally adding contextual information that helps interpret model outputs. Financial Modeling Prep (FMP) provides datasets that align naturally with each of these needs.
The foundation of the forecasting pipeline is historical price data. This dataset provides a time-ordered record of market behavior, typically including open, high, low, close, and volume values.
In the pipeline, historical prices serve as:
This data is consumed by the data agent and passed forward in a structured tabular form. From a workflow perspective, it represents the raw signal the forecasting model attempts to learn from.
While raw prices capture market movement, forecasting models often benefit from derived indicators that summarize recent behavior. Examples include momentum-based indicators, volatility measures, or trend-following signals.
FMP provides precomputed technical indicators that can be retrieved alongside price data. In the pipeline:
Using indicators in this way keeps feature preparation focused on data alignment and transformation rather than indicator math.
Market news does not directly replace quantitative signals, but it provides context for interpreting forecasts. Headlines can help explain why a model-generated trend aligns with recent market events or shifts in sentiment.
Within the pipeline:
This separation ensures that the forecasting model remains grounded in time-series data, while the final output benefits from additional market context.
Each dataset from FMP maps cleanly to a stage in the system:
This separation keeps modeling inputs and contextual data isolated, which helps avoid feature leakage and prevents accidental coupling between the forecasting logic and post-hoc interpretation signals.
With the data foundation defined, the next step is to show how these datasets are organized and processed using a multi-agent design.
With the data foundation in place, the next step is to define how responsibilities are divided across the system. Instead of treating the forecasting pipeline as a single monolithic script, we organize it into a small set of agents, each responsible for a clearly scoped task.
This design keeps the system readable and makes it easier to evolve individual stages without affecting the rest of the pipeline.
The data collection agent is responsible for interacting with FMP datasets. Its role is limited to:
This agent does not perform feature engineering or modeling. By keeping it focused only on data access, changes to data sources or datasets remain isolated from the rest of the system.
From a workflow perspective, this agent converts external financial data into an internal representation that downstream agents can consume reliably.
The feature preparation agent operates entirely on structured data produced by the data agent. Its responsibilities include:
This agent treats feature preparation as a pure transformation step. It does not make assumptions about the forecasting model beyond the shape of inputs it needs to produce. As a result, the same feature pipeline can support different models without modification.
The forecasting agent consumes prepared feature sequences and produces a market trend estimate. In this article, the agent uses an LSTM-based model to capture temporal dependencies in the data.
Importantly, the forecasting agent:
This separation allows the forecasting logic to be swapped or extended independently, while the rest of the pipeline remains unchanged.
All agents communicate through a shared state object that moves sequentially through the pipeline. Each agent reads the inputs it needs from the state, appends its outputs, and passes the state forward.
Execution follows a fixed sequence:
Data Collection → Feature Preparation → Forecasting
This linear execution model keeps control flow explicit and easy to trace. While more advanced routing strategies can be introduced later, a sequential design is sufficient for building and understanding a market trend forecaster on top of FMP data.
With agent responsibilities clearly defined, we can now move from design to implementation, starting with how the data collection agent retrieves market data using FMP APIs.
The forecasting pipeline described in this article can be used at different scales, depending on who is running it and for what purpose. Individual analysts and developers often start with a single symbol and a limited historical window to explore model behavior, test feature ideas, or prototype forecasting logic. In these cases, lightweight usage of FMP market datasets is sufficient to support experimentation and learning.
At the enterprise level, the same structure can be extended to support broader workflows. Research teams may run the pipeline across many symbols, automate recurring forecasts, or integrate outputs into internal dashboards and monitoring systems. As usage scales, considerations such as request volume, data coverage, and operational reliability become more important, which is where different FMP plans and pricing tiers align with varying research and production needs.
This section implements the Data Collection Agent, whose responsibility is to retrieve time-ordered market datasets from FMP and return them in a consistent, analysis-ready form.
The agent focuses strictly on data access and basic normalization, leaving feature engineering and modeling to downstream stages. By the end of this section, you will have a clean, reusable data layer that reliably feeds structured market data into the rest of the forecasting pipeline.
|
from dataclasses import dataclass from typing import Dict, Any import pandas as pd @dataclass class DataAgent: api_key: str def run(self, state: Dict[str, Any]) -> Dict[str, Any]: symbol = state["symbol"] price_df = fetch_price_history(symbol, self.api_key) rsi_df = fetch_technical_indicator( symbol, self.api_key, indicator="rsi", period=14 ) news_df = fetch_stock_news(symbol, self.api_key, limit=5) # Merge later in FeatureAgent; DataAgent just returns datasets. state["price_df"] = price_df state["indicator_df"] = rsi_df state["news_df"] = news_df return state |
What's happening here:
|
import requests def fetch_price_history(symbol: str, api_key: str) -> pd.DataFrame: # Example endpoint pattern used for historical EOD price series url = f"https://financialmodelingprep.com/api/v3/historical-price-full/{symbol}" resp = requests.get(url, params={"apikey": api_key}) resp.raise_for_status() payload = resp.json() rows = payload.get("historical", []) df = pd.DataFrame(rows) if df.empty: return df df["date"] = pd.to_datetime(df["date"]) df = df.sort_values("date") return df[["date", "open", "high", "low", "close", "volume"]] |
One workflow line: This call returns a time-ordered historical price series, which becomes the core signal used for returns, windows, and LSTM training.
Key lines:
|
def fetch_technical_indicator( symbol: str, api_key: str, indicator: str = "rsi", period: int = 14 ) -> pd.DataFrame: # Example endpoint pattern for daily technical indicators url = f"https://financialmodelingprep.com/api/v3/technical_indicator/daily/{symbol}" resp = requests.get( url, params={ "type": indicator, "period": period, "apikey": api_key, }, ) resp.raise_for_status() df = pd.DataFrame(resp.json()) if df.empty: return df df["date"] = pd.to_datetime(df["date"]) df = df.sort_values("date") # Column name typically matches indicator type (e.g., "rsi"), but we guard defensively. value_col = indicator if indicator in df.columns else [ c for c in df.columns if c != "date" ][0] return df[["date", value_col]].rename(columns={value_col: indicator}) |
One workflow line: This call returns a dated indicator series that we align with prices to provide the model an additional signal beyond raw returns.
Key lines:
|
def fetch_stock_news(symbol: str, api_key: str, limit: int = 5) -> pd.DataFrame: # Example endpoint pattern for ticker-filtered news url = "https://financialmodelingprep.com/api/v3/stock_news" resp = requests.get( url, params={ "tickers": symbol, "limit": limit, "apikey": api_key, }, ) resp.raise_for_status() items = resp.json() df = pd.DataFrame(items) # Keep only fields we'll use for context in the final response keep = [c for c in ["publishedDate", "title", "site", "url"] if c in df.columns] return df[keep] if keep else df |
One workflow line: This call returns recent news items for the symbol, which we attach to the forecast output as context—not as a model input.
Key lines:
Now that state holds:
The next step is to align these series and convert them into LSTM-ready windows.
The Data Agent returns structured market datasets—prices, technical indicators, and contextual information—that are ready for transformation. The Feature Preparation stage focuses exclusively on aligning numeric time series, generating targets, and converting the data into fixed-length sequences suitable for time-series modeling. This stage is intentionally model-agnostic: the same feature pipeline can support LSTM models, other neural sequence models, or classical time-series approaches without modification, as long as they consume windowed numeric inputs.
|
import numpy as np import pandas as pd def build_model_table( price_df: pd.DataFrame, indicator_df: pd.DataFrame ) -> pd.DataFrame: # Join on date so every row represents one trading day df = price_df.merge(indicator_df, on="date", how="left") # Basic sanity: ensure chronological order for time-series operations df = df.sort_values("date").reset_index(drop=True) # Compute daily return from close prices (common forecasting signal) df["ret_1d"] = df["close"].pct_change() # Define the prediction target: next-day return df["target"] = df["ret_1d"].shift(-1) # Drop rows with missing values introduced by pct_change/shift or indicator gaps df = df.dropna().reset_index(drop=True) return df |
What's happening in these lines:
LSTM expects a 3D tensor:
|
from sklearn.preprocessing import StandardScaler def make_lstm_windows(df: pd.DataFrame, lookback: int = 30): # Choose a small feature set that maps cleanly to market behavior # - ret_1d: recent movement # - rsi: momentum/overbought-oversold style signal # - volume: activity / participation proxy feature_cols = ["ret_1d", "rsi", "volume"] X_raw = df[feature_cols].values y = df["target"].values # Scale numeric features to stabilize training scaler = StandardScaler() X_scaled = scaler.fit_transform(X_raw) X, Y = [], [] for i in range(lookback, len(df)): X.append(X_scaled[i - lookback:i]) Y.append(y[i]) return np.array(X), np.array(Y), scaler |
What's happening in these lines:
This is a quick guardrail that prevents “silent shape bugs.”
|
def validate_windows(X: np.ndarray, Y: np.ndarray): if X.ndim != 3: raise ValueError(f"Expected X to be 3D, got shape {X.shape}") if Y.ndim != 1: raise ValueError(f"Expected Y to be 1D, got shape {Y.shape}") if X.shape[0] != Y.shape[0]: raise ValueError(f"X and Y sample count mismatch: {X.shape[0]} vs {Y.shape[0]}") |
Why this matters:
At this point, the pipeline has converted raw market data into:
The next step is to train and run the model.
At this stage, the pipeline has already done the heavy lifting on the data side. We have:
Now the forecasting stage trains an LSTM model and produces a trend estimate from the most recent window.
|
from tensorflow.keras import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping def train_lstm_model(X, Y): # X shape: (samples, lookback, features) # Y shape: (samples,) model = Sequential([ LSTM(64, input_shape=(X.shape[1], X.shape[2])), Dropout(0.2), Dense(1) # regression output: predicted return ]) model.compile(optimizer="adam", loss="mse") early_stop = EarlyStopping( monitor="val_loss", patience=3, restore_best_weights=True ) model.fit( X, Y, validation_split=0.2, epochs=20, batch_size=32, callbacks=[early_stop], verbose=0 ) return model |
What's happening in these lines:
|
import numpy as np def predict_trend(model, X): # Use the most recent window as the "current market state" last_window = X[-1:].copy() # shape: (1, lookback, features) pred_return = model.predict(last_window, verbose=0)[0][0] pred_return = float(pred_return) # Convert numeric forecast into a simple trend label trend = "UP" if pred_return > 0 else "DOWN" return { "pred_return": pred_return, "trend": trend } |
What's happening in these lines:
This agent consumes the prepared windows and updates the shared state with a forecast.
|
from dataclasses import dataclass from typing import Dict, Any @dataclass class ForecastAgent: def run(self, state: Dict[str, Any]) -> Dict[str, Any]: X = state["X"] Y = state["Y"] model = train_lstm_model(X, Y) out = predict_trend(model, X) state["model"] = model state["forecast"] = out return state |
What's happening:
At this point, the pipeline has two types of outputs:
The key engineering decision here is to keep these concerns separate:
|
def build_forecast_report(state): symbol = state["symbol"] forecast = state["forecast"] # {"pred_return": ..., "trend": ...} news_df = state.get("news_df") pred_return = forecast["pred_return"] trend = forecast["trend"] # Short, workflow-friendly summary report = { "symbol": symbol, "trend": trend, "predicted_next_return": round(pred_return, 6), "notes": ( "The trend label is derived from the sign of the predicted next-step return " "based on recent price/indicator windows." ), "recent_headlines": [] } # Attach context: keep it lightweight and deterministic if news_df is not None and not news_df.empty: cols = news_df.columns.tolist() title_col = "title" if "title" in cols else None date_col = "publishedDate" if "publishedDate" in cols else None for _, row in news_df.head(5).iterrows(): item = {} if date_col: item["publishedDate"] = row.get(date_col) if title_col: item["title"] = row.get(title_col) report["recent_headlines"].append(item) return report |
What's happening in these lines:
One workflow line (FMP mapping): The news dataset provides recent symbol-linked headlines that we display alongside the forecast as context for interpretation.
|
def run_pipeline(symbol: str, api_key: str): state = {"symbol": symbol} # Agents (linear execution) state = DataAgent(api_key=api_key).run(state) model_df = build_model_table(state["price_df"], state["indicator_df"]) X, Y, scaler = make_lstm_windows(model_df, lookback=30) validate_windows(X, Y) state["model_df"] = model_df state["X"] = X state["Y"] = Y state["scaler"] = scaler state = ForecastAgent().run(state) return build_forecast_report(state) |
What's happening:
The final output is intentionally compact, combining a clear directional signal with the minimum amount of supporting context needed for interpretation. The forecasted trend and predicted return provide a concise quantitative summary, while recent headlines anchor the signal in observable market events.
After the pipeline completes, it produces a single, consolidated forecasting result for the requested symbol. The output contains:
|
{ "symbol": "AMD", "trend": "UP", "predicted_next_return": 0.002341, "notes": "The trend label is derived from the sign of the predicted next-step return based on recent price/indicator windows.", "recent_headlines": [ { "publishedDate": "", "title": "" }, { "publishedDate": "", "title": "" } ] } |
This article showed how a market trend forecaster can be built by treating forecasting as a data and system design problem first, and a modeling problem second. By anchoring the pipeline on structured datasets from Financial Modeling Prep and organizing the workflow into clearly defined agents, the system remains transparent, extensible, and easier to reason about.
The multi-agent structure keeps responsibilities separated across data collection, feature preparation, and forecasting, while the shared state and linear execution model make the pipeline straightforward to debug and evolve. The LSTM-based model fits naturally into this design as a replaceable forecasting component rather than a tightly coupled dependency.
Taken together, this approach produces a forecasting workflow that prioritizes reliable data handling and clean system boundaries, allowing models to be iterated on safely without destabilizing the rest of the pipeline.
Introduction In corporate finance, assessing how effectively a company utilizes its capital is crucial. Two key metri...
Bank of America analysts reiterated a bullish outlook on data center and artificial intelligence capital expenditures fo...
Pinduoduo Inc., listed on the NASDAQ as PDD, is a prominent e-commerce platform in China, also operating internationally...