Skip to content

Add FXMacroData macro provider#1111

Open
roberttidball wants to merge 4 commits into
Lumiwealth:devfrom
roberttidball:codex/lumibot-fxmacrodata-provider
Open

Add FXMacroData macro provider#1111
roberttidball wants to merge 4 commits into
Lumiwealth:devfrom
roberttidball:codex/lumibot-fxmacrodata-provider

Conversation

@roberttidball

@roberttidball roberttidball commented Jul 7, 2026

Copy link
Copy Markdown

Summary

  • add an FXMacroData macro provider for FX-focused announcement series with strategy-date gating and local caching
  • preserve the existing self.macro FRED API through a MacroData container while adding self.macro.fxmacrodata / self.macro.fxmd
  • expose read-only built-in agent tools for FXMacroData series, latest release rows, snapshots, and indicator discovery
  • document FXMD_API_KEY, FXMACRODATA_API_KEY, cache/base-url env vars, and X-API-Key header transport

Validation

  • python -m pytest tests/test_fxmacrodata_macro.py tests/test_fred_macro.py tests/test_agent_tool_permissions.py -q
  • python -m ruff check lumibot/macro/fxmacrodata.py lumibot/macro/macro_data.py tests/test_fxmacrodata_macro.py
  • git diff --check

Summary by CodeRabbit

  • New Features
    • Added built-in FX macro data tools to list indicators, fetch time series, get latest values, and retrieve multi-indicator snapshots.
    • Introduced a unified macro data experience with FXMacroData support alongside FRED, including new MacroData access.
  • Bug Fixes
    • Improved agent guidance and warnings to treat FX macro tool usage as non-order evidence.
    • Ensured FX macro results apply point-in-time filtering and proper caching.
  • Documentation
    • Expanded macro-data docs and environment variable guidance for FXMacroData authentication, headers, and cache/base URL settings.
  • Tests
    • Added coverage for FX macro access, point-in-time filtering, caching, and partial-success snapshots.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5becaaed-b672-42ab-a352-13d6d741d849

📥 Commits

Reviewing files that changed from the base of the PR and between 2b21ef5 and 08f6309.

📒 Files selected for processing (2)
  • lumibot/macro/fxmacrodata.py
  • tests/test_fxmacrodata_macro.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/test_fxmacrodata_macro.py
  • lumibot/macro/fxmacrodata.py

📝 Walkthrough

Walkthrough

This PR adds an FXMacroData provider, a MacroData wrapper, FXMacroData built-in agent tools, agent/runtime wiring, tests, and documentation updates.

Changes

FXMacroData feature

Layer / File(s) Summary
FXMacroData provider implementation
lumibot/macro/fxmacrodata.py
Adds FXMacroData metadata, parsing helpers, request/caching mechanics, indicator listing, series/latest/snapshot retrieval, and observation normalization.
MacroData wrapper and exports
lumibot/macro/macro_data.py, lumibot/macro/__init__.py, lumibot/strategies/_strategy.py
Adds MacroData(FREDMacroData), exposes .fred, .fxmacrodata, and .fxmd, updates package exports, and switches Strategy.macro to load MacroData.
Built-in tools and agent wiring
lumibot/components/agents/builtins.py, lumibot/components/agents/manager.py, lumibot/components/agents/runtime.py
Adds FXMacroData tool binders and definitions, registers them in BuiltinTools.all(), and updates agent warning/prompt logic to recognize the new tool names.
Tests for FXMacroData and MacroData
tests/test_fxmacrodata_macro.py, tests/test_agent_tool_permissions.py
Adds tests for FXMacroData request shaping, caching, credential handling, snapshot aggregation, MacroData aliasing, and FX macro tool permissions.
Documentation updates
docs/AI_AGENT_BUILTIN_TOOLS.md, docs/AI_TRADING_AGENTS.md, docs/ENV_VARS.md, docs/FRED_MACRO_DATA.md, docsrc/agents.rst, docsrc/agents_builtin_tools.rst, docsrc/environment_variables.rst, docsrc/macro_data.rst
Updates macro and agent docs to describe FXMacroData tools, environment variables, FRED section naming, and FXMacroData usage.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • Lumiwealth/lumibot#1021: Extends the same agent tool permission and order_without_data warning classification pathways that this PR updates with FX macro tool names.

Suggested reviewers: grzesir

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the FXMacroData macro provider.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Pylint (4.0.6)
lumibot/macro/fxmacrodata.py

************* Module pylintrc
pylintrc:1:0: F0011: error while parsing the configuration: File contains no section headers.
file: 'pylintrc', line: 1
'known-third-party=lumibot' (config-parse-error)
[
{
"type": "convention",
"module": "lumibot.macro.fxmacrodata",
"obj": "",
"line": 1,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "lumibot/macro/fxmacrodata.py",
"symbol": "missing-module-docstring",
"message": "Missing module docstring",
"message-id": "C0114"
},
{
"type": "warning",
"module": "lumibot.macro.fxmacrodata",
"obj": "_safe_float",
"line": 73,
"column": 11,
"endLine": 73,
"endColumn": 20,
"path": "lumibot/macro/fxmacrodata.py",
"symbol": "broad-exception-caught",
"message": "Catching too general exception Exception",
"message-id": "W0718"
},
{
"type": "warning",

... [truncated 734 characters] ...

age-id": "E1102"
},
{
"type": "refactor",
"module": "lumibot.macro.fxmacrodata",
"obj": "FXMacroData.get_series",
"line": 210,
"column": 4,
"endLine": 210,
"endColumn": 18,
"path": "lumibot/macro/fxmacrodata.py",
"symbol": "too-many-arguments",
"message": "Too many arguments (6/5)",
"message-id": "R0913"
},
{
"type": "refactor",
"module": "lumibot.macro.fxmacrodata",
"obj": "FXMacroData.get_series",
"line": 210,
"column": 4,
"endLine": 210,
"endColumn": 18,
"path": "lumibot/macro/fxmacrodata.py",
"symbol": "too-many-locals",
"message": "Too many local variables (16/15)",
"message-id": "R0914"
}
]

tests/test_fxmacrodata_macro.py

************* Module pylintrc
pylintrc:1:0: F0011: error while parsing the configuration: File contains no section headers.
file: 'pylintrc', line: 1
'known-third-party=lumibot' (config-parse-error)
[
{
"type": "refactor",
"module": "tests.test_fxmacrodata_macro",
"obj": "_Strategy",
"line": 23,
"column": 0,
"endLine": 23,
"endColumn": 15,
"path": "tests/test_fxmacrodata_macro.py",
"symbol": "too-few-public-methods",
"message": "Too few public methods (1/2)",
"message-id": "R0903"
},
{
"type": "refactor",
"module": "tests.test_fxmacrodata_macro",
"obj": "_BacktestingStrategy",
"line": 28,
"column": 0,
"endLine": 28,
"endColumn": 26,
"path": "tests/test_fxmacrodata_macro.py",
"symbol": "too-few-public-methods",
"message": "Too few public methods (1/2)",
"message-id": "R0903"
},
{
"type"

... [truncated 2799 characters] ...

do_not_require_api_key",
"line": 145,
"column": 57,
"endLine": 145,
"endColumn": 76,
"path": "tests/test_fxmacrodata_macro.py",
"symbol": "redefined-outer-name",
"message": "Redefining name 'fxmacrodata_factory' from outer scope (line 55)",
"message-id": "W0621"
},
{
"type": "warning",
"module": "tests.test_fxmacrodata_macro",
"obj": "test_fxmacrodata_snapshot_reports_per_indicator_errors",
"line": 176,
"column": 59,
"endLine": 176,
"endColumn": 78,
"path": "tests/test_fxmacrodata_macro.py",
"symbol": "redefined-outer-name",
"message": "Redefining name 'fxmacrodata_factory' from outer scope (line 55)",
"message-id": "W0621"
}
]


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@roberttidball roberttidball marked this pull request as ready for review July 7, 2026 08:23
@roberttidball roberttidball requested a review from grzesir as a code owner July 7, 2026 08:23

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
lumibot/macro/fxmacrodata.py (1)

167-168: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Non-atomic cache write risks a corrupted cache file.

If the process is interrupted mid-write, cache_path.write_text(...) can leave a partial/corrupt JSON file; every subsequent _get_json call for that path would then raise on json.loads. The codebase already has an established atomic-write pattern (tmp file + os.replace) used elsewhere for exactly this reason (see _strategy.py's _backup_variables_to_scheduled_state_file).

♻️ Proposed atomic write
         cache_path.parent.mkdir(parents=True, exist_ok=True)
-        cache_path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
+        tmp_path = cache_path.with_name(f"{cache_path.name}.{os.getpid()}.tmp")
+        tmp_path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
+        os.replace(tmp_path, cache_path)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lumibot/macro/fxmacrodata.py` around lines 167 - 168, The cache write in the
fx macro data path is non-atomic and can leave a partially written JSON file if
interrupted. Update the cache persistence logic around the write in the relevant
fxmacrodata helper to follow the same tmp-file-plus-os.replace pattern used by
_backup_variables_to_scheduled_state_file in _strategy.py. Keep the existing
payload serialization, but write to a temporary file first, flush it, then
atomically replace cache_path so _get_json never reads a corrupt cache.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/FRED_MACRO_DATA.md`:
- Around line 56-89: The API-key note in the macro data docs is too broad and
incorrectly implies FXMacroData may require FRED credentials. Update the wording
around the macro tools section so the FRED key requirement applies only to the
FRED-specific built-ins, while keeping the FXMacroData section clear that USD
data is public and non-USD access uses FXMD_API_KEY or FXMACRODATA_API_KEY. Use
the existing macro tool names in the docs, such as list_fxmacrodata_indicators
and get_fxmacrodata_latest, to keep the scope distinction explicit.

In `@docsrc/macro_data.rst`:
- Around line 63-93: The macro data docs currently overstate the API-key
requirement by implying it applies broadly; scope the key note to FRED-only
tools so readers do not think FXMacroData needs FRED credentials. Update the
wording in the macro documentation around the existing FRED mention so it
clearly applies only to the FRED-backed built-ins, while keeping the FXMacroData
section consistent with its public USD access and separate `FXMD_API_KEY` /
`FXMACRODATA_API_KEY` behavior.

In `@lumibot/macro/fxmacrodata.py`:
- Around line 296-320: Rows without a parseable date are currently slipping
through the point-in-time filter in the observation normalization loop. Update
the logic in the row-processing block that builds normalized observations so
that when both announcement_dt and _parse_dt(row_date) are None, the row is
excluded instead of being appended. Keep the existing gating behavior in place
for parsed dates, and make sure the fix is applied where comparison_dt is
computed before observations.append is called.
- Around line 155-169: The FXMacroData cache is being used unconditionally in
`_get_json`, which can cause live runs to reuse stale same-day responses;
restrict the disk cache to backtest-only flows by conditioning the cache
read/write on the existing backtest indicator used in this module. Also tighten
`_normalize_observations` so rows without both `announcement_datetime` and the
fallback date fields are dropped instead of being emitted as
`point_in_time_safe`, preventing malformed API rows from bypassing the PIT gate.

---

Nitpick comments:
In `@lumibot/macro/fxmacrodata.py`:
- Around line 167-168: The cache write in the fx macro data path is non-atomic
and can leave a partially written JSON file if interrupted. Update the cache
persistence logic around the write in the relevant fxmacrodata helper to follow
the same tmp-file-plus-os.replace pattern used by
_backup_variables_to_scheduled_state_file in _strategy.py. Keep the existing
payload serialization, but write to a temporary file first, flush it, then
atomically replace cache_path so _get_json never reads a corrupt cache.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 07ee2e9b-18cb-4d6c-8d18-0c0c6598d563

📥 Commits

Reviewing files that changed from the base of the PR and between 4bdae61 and 2f7d6c5.

📒 Files selected for processing (17)
  • docs/AI_AGENT_BUILTIN_TOOLS.md
  • docs/AI_TRADING_AGENTS.md
  • docs/ENV_VARS.md
  • docs/FRED_MACRO_DATA.md
  • docsrc/agents.rst
  • docsrc/agents_builtin_tools.rst
  • docsrc/environment_variables.rst
  • docsrc/macro_data.rst
  • lumibot/components/agents/builtins.py
  • lumibot/components/agents/manager.py
  • lumibot/components/agents/runtime.py
  • lumibot/macro/__init__.py
  • lumibot/macro/fxmacrodata.py
  • lumibot/macro/macro_data.py
  • lumibot/strategies/_strategy.py
  • tests/test_agent_tool_permissions.py
  • tests/test_fxmacrodata_macro.py

Comment thread docs/FRED_MACRO_DATA.md
Comment thread docsrc/macro_data.rst Outdated
Comment thread lumibot/macro/fxmacrodata.py
Comment thread lumibot/macro/fxmacrodata.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/test_fxmacrodata_macro.py (1)

73-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract repeated env-var/monkeypatch/FXMacroData setup into a fixture.

The pattern of clearing FXMD_API_KEY/FXMACRODATA_API_KEY, patching requests.get, and constructing FXMacroData(..., min_request_interval_seconds=0) is duplicated verbatim across multiple tests in this file. Consolidating into a pytest fixture (parametrized by the strategy/payload generator) would reduce boilerplate and make future changes (e.g., adding a new required env var) a one-line fixture edit instead of a multi-site update.

♻️ Example fixture sketch
+@pytest.fixture
+def fxmd_env(monkeypatch):
+    monkeypatch.delenv("FXMD_API_KEY", raising=False)
+    monkeypatch.delenv("FXMACRODATA_API_KEY", raising=False)
+    def _make(strategy, tmp_path, fake_get):
+        monkeypatch.setattr("lumibot.macro.fxmacrodata.requests.get", fake_get)
+        return FXMacroData(strategy, cache_dir=tmp_path, min_request_interval_seconds=0)
+    return _make

Also applies to: 95-102, 116-123

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_fxmacrodata_macro.py` around lines 73 - 82, The FXMacroData test
setup is duplicated across multiple tests, including clearing FXMD_API_KEY and
FXMACRODATA_API_KEY, patching requests.get, and creating FXMacroData with
min_request_interval_seconds=0. Extract that repeated initialization into a
pytest fixture in test_fxmacrodata_macro.py, and have tests like
test_fxmacrodata_drops_rows_without_parseable_dates use the shared fixture while
only varying the payload/strategy inputs. This will centralize the environment
and network mocking around FXMacroData and make future changes easier.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/test_fxmacrodata_macro.py`:
- Around line 73-82: The FXMacroData test setup is duplicated across multiple
tests, including clearing FXMD_API_KEY and FXMACRODATA_API_KEY, patching
requests.get, and creating FXMacroData with min_request_interval_seconds=0.
Extract that repeated initialization into a pytest fixture in
test_fxmacrodata_macro.py, and have tests like
test_fxmacrodata_drops_rows_without_parseable_dates use the shared fixture while
only varying the payload/strategy inputs. This will centralize the environment
and network mocking around FXMacroData and make future changes easier.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4cc8630d-6a62-4286-a721-e24c5bd62c2e

📥 Commits

Reviewing files that changed from the base of the PR and between 2f7d6c5 and 2b21ef5.

📒 Files selected for processing (4)
  • docs/FRED_MACRO_DATA.md
  • docsrc/macro_data.rst
  • lumibot/macro/fxmacrodata.py
  • tests/test_fxmacrodata_macro.py
✅ Files skipped from review due to trivial changes (2)
  • docsrc/macro_data.rst
  • docs/FRED_MACRO_DATA.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant