Time Series Forecasting App - Amazon Chronos-2
Building a production forecasting application without the complexity of traditional ML model training and feature engineering.
The Problem
Time series forecasting traditionally requires:
- Extensive feature engineering
- Model training on historical data
- Hyperparameter tuning
- Ongoing model retraining as patterns change
For many use cases, this complexity isnβt justified. You just need reasonable forecasts without becoming a machine learning team.
The Solution
Built a forecasting application using Amazonβs Chronos-2 foundation model β a 120M parameter model that provides zero-shot probabilistic forecasting. It works on any time series without task-specific training.
The stack:
- Frontend: React 19 + Cloudscape Design System
- Backend: AWS Amplify Gen 2 (Cognito, AppSync, DynamoDB)
- ML: SageMaker endpoint running Chronos-2
How It Works
Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FRONTEND β
β React 19 + Cloudscape Design System β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AWS AMPLIFY GEN 2 β
β βββββββββββββββ ββββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β Amplify β β Cognito β β AppSync β β DynamoDB β β
β β Hosting β β Auth β β GraphQL β β Tables β β
β βββββββββββββββ ββββββββββββββββ ββββββββββββββββ βββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AWS LAMBDA β
β chronos-forecast-handler (Node.js) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AMAZON SAGEMAKER β
β Chronos-2 Real-time Inference Endpoint β
β (ml.g5.xlarge GPU instance) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Lambda β SageMaker Invocation
The Lambda function handles the AppSync GraphQL request and invokes SageMaker:
import { SageMakerRuntimeClient, InvokeEndpointCommand } from "@aws-sdk/client-sagemaker-runtime";
const sagemakerRuntime = new SageMakerRuntimeClient({ region: "eu-west-1" });
export async function generateForecast(routeId: string, historicalPrices: number[]) {
const payload = {
inputs: [{
item_id: routeId,
target: historicalPrices,
}],
parameters: {
prediction_length: 4,
output_type: "quantiles",
quantiles: ["0.1", "0.5", "0.9"] // Confidence intervals
}
};
const command = new InvokeEndpointCommand({
EndpointName: process.env.SAGEMAKER_ENDPOINT,
ContentType: "application/json",
Body: JSON.stringify(payload),
});
const response = await sagemakerRuntime.send(command);
return JSON.parse(new TextDecoder().decode(response.Body));
}
Amplify Gen 2 Schema
Define the GraphQL schema with custom queries:
// amplify/data/resource.ts
export const data = defineData({
schema: `
type Route @model @auth(rules: [{ allow: private }]) {
routeId: ID! @primaryKey
name: String!
historicalPrices: [Float!]!
}
type ForecastResult {
routeId: ID!
predictions: [PredictionPoint!]!
}
type PredictionPoint {
period: String!
p10: Float!
p50: Float!
p90: Float!
}
type Query {
generateForecast(routeId: ID!): ForecastResult
@function(name: "chronos-forecast")
@auth(rules: [{ allow: private }])
}
`,
});
Visualizing Probabilistic Forecasts
Display P10/P50/P90 confidence intervals with Recharts:
import { LineChart, Line, Area, XAxis, YAxis, Tooltip } from "recharts";
function ForecastChart({ data }) {
return (
<LineChart data={data}>
<XAxis dataKey="period" />
<YAxis tickFormatter={(v) => `$${v}`} />
{/* Confidence interval band */}
<Area dataKey="p90" stroke="none" fill="#e3f2fd" fillOpacity={0.5} />
<Area dataKey="p10" stroke="none" fill="#ffffff" />
{/* Median forecast line */}
<Line dataKey="p50" stroke="#1976d2" strokeWidth={2} dot={{ r: 4 }} />
<Tooltip
formatter={(value, name) => [
`$${value.toFixed(2)}`,
name === "p50" ? "Forecast" : name === "p10" ? "Lower bound" : "Upper bound"
]}
/>
</LineChart>
);
}
Accuracy Metrics
Track forecast quality:
// MAPE - Mean Absolute Percentage Error
function calculateMAPE(actual: number[], predicted: number[]): number {
const sumAbsPercentError = actual.reduce((sum, val, i) => {
if (val === 0) return sum;
return sum + Math.abs((val - predicted[i]) / val);
}, 0);
return (sumAbsPercentError / actual.length) * 100;
}
// Interval Coverage - % of actuals within P10-P90
function calculateCoverage(actual: number[], p10: number[], p90: number[]): number {
const withinInterval = actual.filter(
(val, i) => val >= p10[i] && val <= p90[i]
).length;
return (withinInterval / actual.length) * 100;
}
What I Learned
- Chronos-2 simplifies forecasting β No feature engineering or model training required
- Amplify Gen 2 accelerates full-stack development β Schema-driven backend with TypeScript
- Probabilistic forecasts are more useful β P10/P50/P90 enables risk-aware decisions instead of false precision
- Proper backtesting is essential β Walk-forward validation with actual model predictions, not just historical fit
Whatβs Next
- Implement SageMaker Model Monitor for data drift detection
- Add comparison view against naive forecasts (last value, moving average)
- Build MLOps pipeline for model updates
Related Posts
Browser Automation Agents - Amazon Bedrock AgentCore
Enterprise workflows often require interacting with web applications that lack APIs. Traditional automation scripts are brittle and break when UIs change.
DevelopmentAWS Backup Cost Analysis
EBS snapshot costs were growing month-over-month with no clear explanation or optimization strategy.
DevelopmentManaging Local Storage in the AI Development Era
How to identify, clean, and monitor the hidden storage consumers that come with AI-assisted development tools like Claude Code and Kiro
