diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index 1f4463c96d9..fa0471754b7 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -1377,6 +1377,10 @@ export const SIDEBAR: Partial> = { title: "Architecture", url: "data-streams/architecture", }, + { + title: "How Report Timestamps Work", + url: "data-streams/how-report-timestamps-work", + }, { title: "Best Practices", url: "data-streams/concepts/best-practices", diff --git a/src/content/data-streams/architecture.mdx b/src/content/data-streams/architecture.mdx index 94667261fd5..4d85cf2d29e 100644 --- a/src/content/data-streams/architecture.mdx +++ b/src/content/data-streams/architecture.mdx @@ -8,6 +8,7 @@ metadata: whatsnext: { "Learn more about the Standard API Implementation": "/data-streams/tutorials/go-sdk-fetch", + "Learn how report timestamps work": "/data-streams/how-report-timestamps-work", "Find the list of available Stream IDs": "/data-streams/crypto-streams", "Find the schema of data to expect from Data Streams reports: Crypto": "/data-streams/reference/report-schema-v3", "Find the schema of data to expect from Data Streams reports: RWA": "/data-streams/reference/report-schema-v8", diff --git a/src/content/data-streams/concepts/best-practices.mdx b/src/content/data-streams/concepts/best-practices.mdx index 8fd2bf4e66b..c90ef8410e3 100644 --- a/src/content/data-streams/concepts/best-practices.mdx +++ b/src/content/data-streams/concepts/best-practices.mdx @@ -27,6 +27,7 @@ whatsnext: "Learn about Data Streams market hours and schedules": "/data-streams/market-hours", "Learn how to handle market events": "/data-streams/rwa-streams/handling-market-events", "Learn how to handle stock splits": "/data-streams/tokenized-asset-streams/handling-stock-splits", + "Learn how report timestamps work": "/data-streams/how-report-timestamps-work", "Learn about Data Streams rate limits and fair use policy": "/data-streams/rate-limits", } --- @@ -53,6 +54,10 @@ For best practices regarding RWA streams: For best practices regarding stock splits and reverse splits for tokenized assets, please see the [Handling Stock Splits](/data-streams/tokenized-asset-streams/handling-stock-splits) guide. +## Report Timestamps + +Each report applies to a span of time defined by `validFromTimestamp` and `observationsTimestamp`, and that span can vary in width. Read both fields from each report rather than assuming a fixed cadence or deriving the window from your own clock. See [How Report Timestamps Work](/data-streams/how-report-timestamps-work) for details, including how gaps between reports are absorbed and how timestamps apply to TWAP streams. + ## Rate Limits Rely on the WebSocket stream for report delivery rather than polling a REST endpoint in a tight loop, and implement exponential backoff on reconnect logic. See the [Rate Limits and Fair Use Policy](/data-streams/rate-limits) page for applicable limits and how to avoid HTTP 429 responses. diff --git a/src/content/data-streams/how-report-timestamps-work.mdx b/src/content/data-streams/how-report-timestamps-work.mdx new file mode 100644 index 00000000000..3d94f4c47bc --- /dev/null +++ b/src/content/data-streams/how-report-timestamps-work.mdx @@ -0,0 +1,83 @@ +--- +section: dataStreams +date: "Last Modified" +title: "How Report Timestamps Work" +metadata: + title: "How Chainlink Data Streams Report Timestamps Work | validFromTimestamp and observationsTimestamp" + description: "Learn how Chainlink Data Streams report timestamps define a span of time rather than an instant, how gaps between reports are absorbed, and how timestamps apply to TWAP streams." + keywords: + [ + "Data Streams", + "Timestamps", + "validFromTimestamp", + "observationsTimestamp", + "TWAP", + "Time Weighted Average Price", + "Reports", + ] +whatsnext: + { + "See the REST API reference": "/data-streams/reference/data-streams-api/interface-api", + "See the WebSocket reference": "/data-streams/reference/data-streams-api/interface-ws", + "Learn about Data Streams architecture": "/data-streams/architecture", + "Review Data Streams best practices": "/data-streams/concepts/best-practices", + } +--- + +import { Aside } from "@components" +import DataStreams from "@features/data-streams/common/DataStreams.astro" + + + +Every Data Streams report carries two timestamps: + +- **`validFromTimestamp`** — when the report's price _starts being valid_ +- **`observationsTimestamp`** — when the report's price _was last observed_. + +Together, they describe a window of time rather than a single instant. This page explains how the two timestamps define a [report's window](#report-windows), what to expect when [no report exists for a given moment](#determining-current-price), and how timestamps apply to [Time Weighted Average Price (TWAP) streams](#twap-streams). + +## Report windows + +A report does not have verified price for a single instant in time. The `validFromTimestamp` marks the beginning of the window, and the `observationsTimestamp` marks the end. + +Both fields are Unix timestamps in seconds. [Each report schema](/data-streams/reference/report-schema-overview) contains definitions, including the unit of time, for all fields. + +The window of time can vary depending on how frequently the Decentralized Oracle Network (DON) can observe the data and generate reports. Windows are contiguous by construction: each one starts immediately following the previous window. Each report is designed to have no gaps or overlap, with every time interval belonging to exactly one report. + +## Determining current price + +Because each report covers a window rather than an instant, the "current" price at any given moment is the price of the report whose window contains that moment. Whether such a report exists yet depends on how recently the DON observed the data and delivered the report. + +**Normal case: a report exists at that exact moment** + +For example, a report exists with `observationsTimestamp = 12:05:00` and a 1-second window. The report is delivered slightly after `12:05:00` (for reports with second-precision timestamps, typically up to 1-2 seconds later) due to DON consensus and transmission. + +**Edge case: no report at that exact moment** + +It is possible for no report to be observed with `observationsTimestamp = 12:05:00`. For example: + +- Last report: `observationsTimestamp = 12:04:59` +- Next report: `observationsTimestamp = 12:05:01` + +The next report's `validFromTimestamp` is `12:05:00`, absorbing the gap and creating a 2-second window (`12:05:00` → `12:05:01`). In this example, the price for `12:05:00` is observed one second later at `12:05:01`. + +Because windows can vary in width, **do not assume every report covers the same window**. Read `validFromTimestamp` and `observationsTimestamp` from each report rather than deriving the window from the previous report's timestamp or from your own clock. + +## TWAP streams + +Time Weighted Average Price (TWAP) streams report the average price over a rolling window, rather than a single observed price. A TWAP report is an **aggregated calculation** derived from the underlying reports. See the [TWAP report schema](/data-streams/reference/report-schema-v2#time-weighted-average-price-twap) for the full field definitions. + +A TWAP window is anchored to the latest observation and moves forward with each new report. Unlike standard streams, it doesn't align to the clock or snap to round numbers. + +For example, a 60-second TWAP requested "at `12:05:01`" covers `12:04:01` → `12:05:01`. + +**Formula:** + +``` +TWAP = (sum of price × duration for each report in the window) / window length in seconds +``` + +- 30-second TWAP → divide by 30 +- 60-second TWAP → divide by 60 + +The TWAP is constructed from the underlying reports that fall within its window. Each underlying report normally covers about 1 second, but if a report's window is wider because it absorbed a gap, its price is weighted across the full window it covers. diff --git a/src/content/data-streams/llms-full.txt b/src/content/data-streams/llms-full.txt index 536f675b8ee..611e842c3c0 100644 --- a/src/content/data-streams/llms-full.txt +++ b/src/content/data-streams/llms-full.txt @@ -402,6 +402,10 @@ For best practices regarding RWA streams: For best practices regarding stock splits and reverse splits for tokenized assets, please see the [Handling Stock Splits](/data-streams/tokenized-asset-streams/handling-stock-splits) guide. +## Report Timestamps + +Each report applies to a span of time defined by `validFromTimestamp` and `observationsTimestamp`, and that span can vary in width. Read both fields from each report rather than assuming a fixed cadence or deriving the window from your own clock. See [How Report Timestamps Work](/data-streams/how-report-timestamps-work) for details, including how gaps between reports are absorbed and how timestamps apply to TWAP streams. + ## Rate Limits Rely on the WebSocket stream for report delivery rather than polling a REST endpoint in a tight loop, and implement exponential backoff on reconnect logic. See the [Rate Limits and Fair Use Policy](/data-streams/rate-limits) page for applicable limits and how to avoid HTTP 429 responses. @@ -961,6 +965,66 @@ If you believe you've been rate limited unexpectedly, see the [Rate Limits and F --- +# How Report Timestamps Work +Source: https://docs.chain.link/data-streams/how-report-timestamps-work + + + +Every Data Streams report carries two timestamps: + +- **`validFromTimestamp`** — when the report's price *starts being valid* +- **`observationsTimestamp`** — when the report's price *was last observed*. + +Together, they describe a window of time rather than a single instant. This page explains how the two timestamps define a [report's window](#report-windows), what to expect when [no report exists for a given moment](#determining-current-price), and how timestamps apply to [Time Weighted Average Price (TWAP) streams](#twap-streams). + +## Report windows + +A report does not have verified price for a single instant in time. The `validFromTimestamp` marks the beginning of the window, and the `observationsTimestamp` marks the end. + +Both fields are Unix timestamps in seconds. [Each report schema](/data-streams/reference/report-schema-overview) contains definitions, including the unit of time, for all fields. + +The window of time can vary depending on how frequently the Decentralized Oracle Network (DON) can observe the data and generate reports. Windows are contiguous by construction: each one starts immediately following the previous window. Each report is designed to have no gaps or overlap, with every time interval belonging to exactly one report. + +## Determining current price + +Because each report covers a window rather than an instant, the "current" price at any given moment is the price of the report whose window contains that moment. Whether such a report exists yet depends on how recently the DON observed the data and delivered the report. + +**Normal case: a report exists at that exact moment** + +For example, a report exists with `observationsTimestamp = 12:05:00` and a 1-second window. The report is delivered slightly after `12:05:00` (for reports with second-precision timestamps, typically up to 1-2 seconds later) due to DON consensus and transmission. + +**Edge case: no report at that exact moment** + +It is possible for no report to be observed with `observationsTimestamp = 12:05:00`. For example: + +- Last report: `observationsTimestamp = 12:04:59` +- Next report: `observationsTimestamp = 12:05:01` + +The next report's `validFromTimestamp` is `12:05:00`, absorbing the gap and creating a 2-second window (`12:05:00` → `12:05:01`). In this example, the price for `12:05:00` is observed one second later at `12:05:01`. + +Because windows can vary in width, **do not assume every report covers the same window**. Read `validFromTimestamp` and `observationsTimestamp` from each report rather than deriving the window from the previous report's timestamp or from your own clock. + +## TWAP streams + +Time Weighted Average Price (TWAP) streams report the average price over a rolling window, rather than a single observed price. A TWAP report is an **aggregated calculation** derived from the underlying reports. See the [TWAP report schema](/data-streams/reference/report-schema-v2#time-weighted-average-price-twap) for the full field definitions. + +A TWAP window is anchored to the latest observation and moves forward with each new report. Unlike standard streams, it doesn't align to the clock or snap to round numbers. + +For example, a 60-second TWAP requested "at `12:05:01`" covers `12:04:01` → `12:05:01`. + +**Formula:** + +``` +TWAP = (sum of price × duration for each report in the window) / window length in seconds +``` + +- 30-second TWAP → divide by 30 +- 60-second TWAP → divide by 60 + +The TWAP is constructed from the underlying reports that fall within its window. Each underlying report normally covers about 1 second, but if a report's window is wider because it absorbed a gap, its price is weighted across the full window it covers. + +--- + # Chainlink Data Streams Source: https://docs.chain.link/data-streams @@ -4716,6 +4780,8 @@ All routes require the following three headers for user authentication: ## API endpoints +Report responses include `validFromTimestamp` and `observationsTimestamp`, which together define the span of time the report's price applies to rather than a single instant. See [How Report Timestamps Work](/data-streams/how-report-timestamps-work) for details, including what happens when no report exists for a given moment. + ### Discover data streams ##### Endpoint @@ -6207,7 +6273,7 @@ Cryptocurrency streams adhere to the report schema outlined below. ## Time-Weighted Average Price (TWAP) -The v2 schema is used by TWAP streams. The `price` field carries the time-weighted average price for the stream's configured window, not a spot or median price. Each TWAP stream is scoped to one asset and one window length (for example, 30 seconds or 60 seconds). The window length is part of the stream's name and metadata. +The v2 schema is used by TWAP streams. The `price` field carries the time-weighted average price for the stream's configured window, not a spot or median price. Each TWAP stream is scoped to one asset and one window length (for example, 30 seconds or 60 seconds). The window length is part of the stream's name and metadata. See [How Report Timestamps Work](/data-streams/how-report-timestamps-work) for how timestamps apply to TWAP streams. ## Schema Fields diff --git a/src/content/data-streams/reference/data-streams-api/interface-api.mdx b/src/content/data-streams/reference/data-streams-api/interface-api.mdx index ae2c5073f12..3c9420afd0b 100644 --- a/src/content/data-streams/reference/data-streams-api/interface-api.mdx +++ b/src/content/data-streams/reference/data-streams-api/interface-api.mdx @@ -37,6 +37,8 @@ All routes require the following three headers for user authentication: ## API endpoints +Report responses include `validFromTimestamp` and `observationsTimestamp`, which together define the span of time the report's price applies to rather than a single instant. See [How Report Timestamps Work](/data-streams/how-report-timestamps-work) for details, including what happens when no report exists for a given moment. + ### Discover data streams ##### Endpoint diff --git a/src/content/data-streams/reference/report-schema-v2.mdx b/src/content/data-streams/reference/report-schema-v2.mdx index de1d1debc0a..b2d3b4f3ece 100644 --- a/src/content/data-streams/reference/report-schema-v2.mdx +++ b/src/content/data-streams/reference/report-schema-v2.mdx @@ -20,7 +20,7 @@ Cryptocurrency streams adhere to the report schema outlined below. ## Time-Weighted Average Price (TWAP) -The v2 schema is used by TWAP streams. The `price` field carries the time-weighted average price for the stream's configured window, not a spot or median price. Each TWAP stream is scoped to one asset and one window length (for example, 30 seconds or 60 seconds). The window length is part of the stream's name and metadata. +The v2 schema is used by TWAP streams. The `price` field carries the time-weighted average price for the stream's configured window, not a spot or median price. Each TWAP stream is scoped to one asset and one window length (for example, 30 seconds or 60 seconds). The window length is part of the stream's name and metadata. See [How Report Timestamps Work](/data-streams/how-report-timestamps-work) for how timestamps apply to TWAP streams. ## Schema Fields diff --git a/src/features/feeds/components/reportSchemaData.ts b/src/features/feeds/components/reportSchemaData.ts index dba1c6f7ef5..66b0f420861 100644 --- a/src/features/feeds/components/reportSchemaData.ts +++ b/src/features/feeds/components/reportSchemaData.ts @@ -21,11 +21,17 @@ export interface SchemaDefinition { // Fields present in every report schema const COMMON_FIELDS: SchemaField[] = [ { field: "feedId", type: "bytes32", description: "Unique identifier for the Data Streams feed" }, - { field: "validFromTimestamp", type: "uint32", description: "Earliest timestamp when the price is valid (seconds)" }, + { + field: "validFromTimestamp", + type: "uint32", + description: "Earliest timestamp when the price is valid (seconds)", + link: { label: "How report timestamps work", href: "/data-streams/how-report-timestamps-work" }, + }, { field: "observationsTimestamp", type: "uint32", description: "Latest timestamp when the price is valid (seconds)", + link: { label: "How report timestamps work", href: "/data-streams/how-report-timestamps-work" }, }, { field: "nativeFee", type: "uint192", description: "Legacy onchain verification fee field" }, {