Skip to content

Research guide

Backtest macro events without look-ahead bias

A point-in-time backtest needs the value vintage available at each decision cutoff, evidence for its availability, and compatible forecast and actual series. A forecast's generated_at preceding announcement_datetime is only an ordering check. Use as_of to select eligible history, inspect the timestamp and vintage evidence, and retain the complete responses used in your study.

Forecast eligibility

Ordering and evidence matter

pre_release_only=true checks forecast ordering against the linked release marker. Inspect provenance and use an explicit as_of cutoff to verify historical availability; an archive's date-based marker does not establish an intraday publication instant.

Join key

announcement_id

Stable id shared by /v1/predictions/{currency}/{indicator} and /v1/announcements/{currency}/{indicator}, in the form {currency}_{indicator}_{date}.

Honest coverage

Depth varies by series

Some pairs carry years of monthly pre-release forecasts; others have none. Check the coverage matrix before designing a study.

Check pre-release forecasts at your decision cutoff

Query the predictions endpoint with pre_release_only=true (the default), as_of, and event_compatible_only=true for event studies. Inspect provenance and data_quality: an official publisher archive can preserve a genuine historical forecast value even when FXMacroData imported it later, and its source may provide only a date rather than an original intraday timestamp. Empty eligible history is a coverage limit to preserve in your results.

GET https://api.fxmacrodata.com/v1/predictions/usd/inflation
    ?pre_release_only=true
    &as_of=2026-08-31T12:00:00Z
    &event_compatible_only=true
    &start_date=2013-08-01
    &end_date=2026-08-31
    &limit=100
X-API-Key: YOUR_API_KEY
Always pass explicit dates for historical research. When start_date is omitted the endpoint defaults to roughly the last six months. An empty result for an old window means no eligible pre-release forecast was recorded for those releases — not that the data is hidden behind another parameter or plan.

Every field a backtest join needs

/v1/predictions/{currency}/{indicator}

FieldMeaning
announcement_idJoin key to the realised announcement
announcement_datetimeLinked release marker in Unix epoch seconds (UTC); check the actual release's publication evidence. announcement_datetime_local is an ISO 8601 string with an offset
predictions[].predicted_valueThe forecast, in the units of the selected series
predictions[].prediction_sourceStable source slug (e.g. cleveland_fed_nowcast)
predictions[].prediction_typeWhat the forecast represents — see the type reference
predictions[].generated_atKnown-at marker, epoch seconds (UTC); its source and precision are defined by provenance
predictions[].provenanceMachine-readable value origin, capture mode, timestamp basis and precision, reconstruction flag, and retained-vintage policy

/v1/announcements/{currency}/{indicator}

FieldMeaning
valRealised value of the selected series; variant selectors (e.g. frequency=mom) choose the series, and headline convenience fields such as val_mom appear where published
previous_value / previous_datePrior observation, with change_from_previous computed where that generic comparison is applicable
change (USD NFP)Official BLS monthly payroll change in persons. For NFP, val is the total payroll-employment level and the current forecast series targets val, not change.
revisions[]Retained value vintages and their evidence, where available. Use revisions=all to inspect them, or as_of with revisions=latest to select the latest eligible vintage at a cutoff
announcement_datetimePublication-time marker in epoch seconds (UTC), or null when unknown; it is not a publication guarantee by itself
publication_time_status / publication_time_precisionWhether publication timing is confirmed, unverified, estimated historical, or unknown, and the precision supported by its evidence
vintage_status / is_first_releaseEvidence about the value vintage and first-print status; the earliest captured value is not necessarily the initial release
release_time_assumedTrue for an estimated historical publication time. Exclude these estimates from strict replay; a false or absent flag alone does not establish eligibility
data_quality.assumed_release_time_countHow many rows in the requested window carry a derived timestamp, against data_quality.row_count
data_quality.point_in_time_safeWhether returned values have verified availability and vintage evidence. Publication timestamps alone are insufficient; inspect this flag together with the selected cutoff, clock, and row evidence

Publication, capture, and availability are different clocks

Use availability=public for source-publication replay and availability=captured for values FXMacroData had captured by your cutoff. Neither proves when a customer could read the value through the API. An official schedule remains a planned time. A publisher's declared timestamp, a date-only archive, and a precisely recorded collection time provide different evidence; nanosecond representation does not establish nanosecond publication accuracy. Strict intraday source-public replay requires the availability evidence accepted by the selector, not just publication_time_status=confirmed.

Estimated historical publication times are allowed only when both the reference period and estimated publication precede 6 November 2025. They carry publication_time_status=assumed_historical and release_time_assumed=true and are not eligible for strict replay. Later observations without publication evidence remain available with publication_time_status=unknown. Check coverage for your chosen window instead of assuming that every observation has a usable publication time or an original first-print value.

How the historical forecast values were obtained

FXMacroData model forecasts

capture_mode=stored_at_generation

These are stored when the model runs, with the model-run timestamp. Historical rows are the stored outputs, not reruns made with a current model or later actuals. For the five US series discussed here — headline CPI, core CPI, NFP, unemployment and average hourly earnings — the durable stored archive currently begins with August 2026 reference periods; older model outputs are unavailable and have not been reconstructed.

Cleveland Fed nowcasts

capture_mode=retrospective_official_archive_import

These are genuine values from the Cleveland Fed's historical publisher archive, imported retrospectively by FXMacroData. They were not captured by FXMacroData in real time and are not recreated with a newer model. The feed retains the final pre-release point for each target month, not every daily vintage. Because the archive supplies a source date, generated_at is that date normalized to the source's 10:00 ET update time.

FOMC SEP and New York Fed SME rows likewise come from official publisher archives with date-based known-at markers. Read predictions[].provenance on each response. If that object is absent, the source's capture semantics have not yet been audited for this public contract and should not be assumed.

What pre-release history actually exists (USD)

History depends on the publisher archive and the vintages retained by FXMacroData. Use this source overview alongside the coverage matrix, then check the eligible rows for your own cutoff:

Event familyPre-release sourceHistory
CPI and Core CPI (y/y and m/m)Cleveland Fed Inflation NowcastingRetained final pre-release monthly nowcasts; the archive does not contain the full daily vintage path. Date-based archive markers do not establish strict intraday availability
Unemployment rateFOMC SEP medians + NY Fed Survey of Market ExpectationsOfficial projections retain their native target periods. Check event compatibility before comparing them with a monthly unemployment release; shared indicator names alone do not establish a match
FOMC rate decisionAtlanta Fed Market Probability TrackerSparse — roughly three meetings per year from September 2023
Non-farm payrolls, average hourly earnings, PPI, retail sales—No historical event-specific market consensus is available, on any plan. Stored FXMacroData model forecasts are a separate source, not consensus.

There is no separate hidden market_consensus feed for monthly US CPI or NFP on any paid plan. Plan access changes the permitted history window and currency coverage; it does not add prediction sources that are absent from the stored series.

FXMacroData blended forecasts are available where a stored eligible forecast exists. A scheduled announcement does not guarantee a forecast. For non-USD coverage, see the full source-labelled coverage matrix.

Pulling complete history at a decision cutoff

Pass explicit period dates and an as_of decision time. The example downloads all eligible pages for one cutoff, using before_date for forecasts and pagination.next_offset for announcements. It preserves source-unit announcement values with value_mode=source and series_mode=raw. Repeat cutoff selection for each decision in your study; start_date and end_date alone do not protect against later revisions.

import requests

BASE = "https://api.fxmacrodata.com/v1"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
COMMON = {"start_date": "2013-08-01", "end_date": "2026-08-31",
          "as_of": "2026-08-31T12:00:00Z", "limit": 100}

preds, before_date = [], None
while True:
    params = {**COMMON, "pre_release_only": "true",
              "event_compatible_only": "true"}
    if before_date:
        params["before_date"] = before_date
    response = requests.get(
        f"{BASE}/predictions/usd/inflation", headers=HEADERS, params=params,
        timeout=30,
    )
    response.raise_for_status()
    page = response.json()
    if page["data"] and not page["data_quality"]["point_in_time_safe"]:
        raise ValueError("Forecast availability is not verified for this cutoff")
    preds.extend(page["data"])
    if not page["has_more"]:
        break
    before_date = page["next_cursor"]

actuals, offset, version = [], 0, None
while True:
    params = {**COMMON, "revisions": "latest", "availability": "public",
              "value_mode": "source", "series_mode": "raw", "offset": offset}
    if version:
        params["dataset_version"] = version
    response = requests.get(
        f"{BASE}/announcements/usd/inflation",
        headers=HEADERS,
        params=params,
        timeout=30,
    )
    response.raise_for_status()
    page = response.json()
    version = page["dataset_version"]
    if (page.get("value_mode") != "source"
            or page.get("value_metadata", {}).get("normalization_applied") is not False):
        raise ValueError("Source-unit values were not confirmed")
    if page["data"] and not page["data_quality"]["point_in_time_safe"]:
        raise ValueError("Value-vintage availability is not verified for this cutoff")
    actuals.extend(page["data"])
    pagination = page["pagination"]
    if not pagination["has_more"]:
        break
    offset = pagination["next_offset"]

inputs_by_id = {row["announcement_id"]: row for row in actuals}

An empty result means there are no eligible observations for these settings. dataset_version detects changes between announcement pages; a version conflict requires restarting the download. It is a content fingerprint, not an immutable archive. Retain your downloaded forecast and announcement responses, request settings, and hashes to preserve the evidence used in the study.

These are inputs known at the chosen cutoff. To measure an announcement surprise, separately select an evidenced first-release actual and a compatible pre-release forecast, then verify their units and target period. A latest revised actual must not silently stand in for a first print. See the response field reference for the evidence fields.

Use the same series selectors on both endpoints only when the predictions response advertises that variant in supported_options. CPI supports matching m/m nowcasts. USD NFP does not: the announcement returns the employment level as val and the official jobs gain or loss as change, while the current predictions endpoint forecasts only the level and therefore advertises frequency=level.

Access for historical research

Without a key

USD announcements are open for evaluation over the most recent 90 days, up to 100 requests per day, delivered 15 minutes after publication. Forecast data requires an API key on every currency, USD included.

Individual plan

Full stored history for every supported currency and indicator, including all pre-release forecast rows that FXMacroData actually stores. Every paid plan receives the same available data — there is no higher tier holding back deeper history, and a paid plan does not create forecast rows that do not exist.

AI Answer-Ready

Key Facts

Page
Point In Time Backtesting
Section
Documentation
Canonical URL
https://fxmacrodata.com/documentation/point-in-time-backtesting
Source
FXMacroData editorial and official publisher references
Last Updated
See page metadata

Provenance And Trust

Cite the canonical URL and source field above. Where available, this page maps to official publisher releases and timestamped updates.

Quick Q&A

What is this page about? This page explains Point In Time Backtesting with directly usable context for trading, research, and API workflows.

What source should be cited? Use the canonical URL and the listed source field; cite official publisher references when available.

How fresh is this content? The last updated value above reflects the page metadata or latest available data timestamp.

Can this be used in AI assistants? Yes. This section is intentionally structured for retrieval and citation in chat assistants.

Prompt Packs

Use these in ChatGPT, Claude, Gemini, Mistral, Perplexity, or Grok for consistent source-aware outputs.