From 00b0e5aeedd1afa699889e15171952192e961f9a Mon Sep 17 00:00:00 2001 From: Karl Waldman Date: Sun, 13 Sep 2026 17:05:16 -0400 Subject: [PATCH 1/2] fix(models): type SubscriptionEvent from the event the API sends (#149) SubscriptionEvent declared type, code, payload and created_at, none of which GET /v1/subscriptions/events sends, so they read None on every real event, while id, observed_at, snapshot, deltas, source and tool_name were untyped pydantic extras. - Required id, seq, watch_id, observed_at (datetime), snapshot and deltas, matching null: false in the API's watch_events schema; optional source and tool_name (nullable columns). - snapshot -> Dict[str, SubscriptionEventSnapshot] (price, currency, optional change_24h_pct / as_of); deltas -> Dict[str, SubscriptionEventDelta] (price_change, optional pct_change, which the API omits for a zero prior price). Both exported. - type, code and payload removed: no event field corresponds to them. created_at kept as a deprecated property returning observed_at. - unwrap_events_page drops its seq-is-None guard; seq is now required. - Existing event fixtures updated to the live shape. Closes #149 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015ao5paex73xXvuM424Libo --- CHANGELOG.md | 18 ++ oilpriceapi/__init__.py | 4 + oilpriceapi/_subscriptions_common.py | 2 +- oilpriceapi/models.py | 86 +++++-- .../unit/test_async_subscriptions_resource.py | 13 +- tests/unit/test_subscription_event_model.py | 210 ++++++++++++++++++ .../test_subscriptions_list_events_strict.py | 4 +- tests/unit/test_subscriptions_resource.py | 19 +- 8 files changed, 328 insertions(+), 28 deletions(-) create mode 100644 tests/unit/test_subscription_event_model.py diff --git a/CHANGELOG.md b/CHANGELOG.md index ad0c740..b6b39f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,24 @@ All notable changes to the OilPriceAPI Python SDK will be documented in this fil ### Fixed +- **`SubscriptionEvent` is typed from the event the API sends (#149).** It + declared `type`, `code`, `payload` and `created_at`, which + `GET /v1/subscriptions/events` has never sent, so they read `None` on every + real event. Meanwhile `id`, `observed_at`, `snapshot`, `deltas`, `source` and + `tool_name` were untyped extras. The model now declares: + - required `id`, `seq`, `watch_id`, `observed_at` (a timezone-aware + `datetime`), `snapshot` and `deltas`; + - optional `source` and `tool_name`. + `snapshot` maps each code to the new `SubscriptionEventSnapshot` (`price`, + `currency`, optional `change_24h_pct` and `as_of`). `deltas` maps each code + to the new `SubscriptionEventDelta` (`price_change`, optional `pct_change`). + An event missing a required field raises + `OilPriceAPIError(code="MALFORMED_RESPONSE")`. + - **Behaviour change:** `type`, `code` and `payload` are removed, because no + field in the event corresponds to them. Code that read them only ever got + `None`. Watched codes are the keys of `event.snapshot`. + - `created_at` is kept as a deprecated property. It returns `observed_at` and + emits a `DeprecationWarning`. - **`subscriptions.list()` and `subscriptions.events()` no longer report a malformed success as "nothing there" (#142), sync and async.** A 200 without a `data.subscriptions` list returned `[]`, and one without `data.events` / diff --git a/oilpriceapi/__init__.py b/oilpriceapi/__init__.py index 7d767f4..1fc6212 100644 --- a/oilpriceapi/__init__.py +++ b/oilpriceapi/__init__.py @@ -45,6 +45,8 @@ PriceAlert, Subscription, SubscriptionEvent, + SubscriptionEventDelta, + SubscriptionEventSnapshot, WebhookTestResponse, ) from oilpriceapi.resources.subscriptions import SubscriptionEventsPage @@ -90,6 +92,8 @@ "ParcelFuelSurchargeCarrier", "Subscription", "SubscriptionEvent", + "SubscriptionEventDelta", + "SubscriptionEventSnapshot", "SubscriptionEventsPage", "PriceStream", "StreamUpdate", diff --git a/oilpriceapi/_subscriptions_common.py b/oilpriceapi/_subscriptions_common.py index be2cf55..12439f5 100644 --- a/oilpriceapi/_subscriptions_common.py +++ b/oilpriceapi/_subscriptions_common.py @@ -386,7 +386,7 @@ def unwrap_events_page( f"{_field_errors(error)}", response, ) from error - if event.seq is not None and event.seq > cursor: + if event.seq > cursor: raise _malformed( subject, f"data.cursor {cursor} is behind data.events[{index}].seq {event.seq}; " diff --git a/oilpriceapi/models.py b/oilpriceapi/models.py index 3a7eaf9..4d1ed6f 100644 --- a/oilpriceapi/models.py +++ b/oilpriceapi/models.py @@ -4,6 +4,7 @@ Pydantic models for API responses. """ +import warnings from datetime import date, datetime from typing import Any, Dict, List, Optional, Union @@ -454,32 +455,75 @@ def parse_datetimes(cls, v): return v -class SubscriptionEvent(BaseModel): - """A single event emitted by a subscription, returned from the poll endpoint.""" +class SubscriptionEventSnapshot(BaseModel): + """One watched code's price at the moment a subscription event was recorded. + + Built by the API's ``MarketBriefBuilder#snapshot_hash``. + """ model_config = ConfigDict(populate_by_name=True, extra="allow") - seq: Optional[int] = Field(default=None, description="Monotonic per-user sequence cursor") - watch_id: Optional[str] = Field(default=None, description="Subscription (watch) that produced the event") - type: Optional[str] = Field(default=None, description="Event type") - code: Optional[str] = Field(default=None, description="Commodity code the event relates to") - payload: Optional[Dict[str, Any]] = Field(default=None, description="Event payload") - created_at: Optional[datetime] = Field(default=None, description="Event timestamp") + price: float = Field(description="Latest spot price") + currency: str = Field(description="Price currency, e.g. USD") + change_24h_pct: Optional[float] = Field( + default=None, description="24h change in percent; None when there is no 24h comparison" + ) + as_of: Optional[datetime] = Field(default=None, description="Timestamp of the price used") - @field_validator("created_at", mode="before") - @classmethod - def parse_created_at(cls, v): - """Parse created_at from various formats.""" - if v is None: - return None - if isinstance(v, str): - try: - return datetime.fromisoformat(v.replace("Z", "+00:00")) - except ValueError: - from dateutil import parser - return parser.parse(v) - return v +class SubscriptionEventDelta(BaseModel): + """One code's change since the previous event of the same subscription. + + Built by the API's ``Watch#compute_deltas``. + """ + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + price_change: float = Field(description="Price change since the previous event") + pct_change: Optional[float] = Field( + default=None, + description="Percent change; None when the previous price was 0 (the API omits it)", + ) + + +class SubscriptionEvent(BaseModel): + """A single event emitted by a subscription, returned from the poll endpoint. + + Typed from ``GET /v1/subscriptions/events`` as the API sends it (#149). + ``snapshot`` and ``deltas`` are keyed by commodity code. ``deltas`` is + ``{}`` on a subscription's first event, and a code is absent from it when + either snapshot lacked a price. + """ + + model_config = ConfigDict(populate_by_name=True, extra="allow") + + id: str = Field(description="Event identifier") + seq: int = Field(description="Monotonic per-user sequence cursor") + watch_id: str = Field(description="Subscription (watch) that produced the event") + observed_at: datetime = Field(description="When the snapshot was taken") + snapshot: Dict[str, SubscriptionEventSnapshot] = Field( + description="Price per watched code at observed_at" + ) + deltas: Dict[str, SubscriptionEventDelta] = Field( + description="Change per code since the previous event; {} on the first event" + ) + source: Optional[str] = Field(default=None, description="Attribution source of the subscription") + tool_name: Optional[str] = Field(default=None, description="Attribution tool name of the subscription") + + @property + def created_at(self) -> datetime: + """Deprecated alias for ``observed_at``. + + The API never sent ``created_at`` on an event, so this always read + ``None``. The event's timestamp is ``observed_at``. + """ + warnings.warn( + "SubscriptionEvent.created_at is deprecated and will be removed; use " + "observed_at. The events API does not send created_at (#149).", + DeprecationWarning, + stacklevel=2, + ) + return self.observed_at class DataConnectorPrice(BaseModel): diff --git a/tests/unit/test_async_subscriptions_resource.py b/tests/unit/test_async_subscriptions_resource.py index 8adccfb..0a95710 100644 --- a/tests/unit/test_async_subscriptions_resource.py +++ b/tests/unit/test_async_subscriptions_resource.py @@ -73,7 +73,18 @@ async def test_events(self, client): "data": { "cursor": 7, "has_more": False, - "events": [{"seq": 7, "watch_id": "abc-123", "type": "threshold", "code": "WTI_USD"}], + "events": [ + { + "id": "a835a930-f34f-4001-80b1-38fb3cde3797", + "seq": 7, + "watch_id": "abc-123", + "observed_at": "2026-09-08T17:21:19Z", + "snapshot": {"WTI_USD": {"as_of": "2026-09-08T17:20:37Z", "price": 63.02, "currency": "USD", "change_24h_pct": -0.08}}, + "deltas": {"WTI_USD": {"pct_change": -0.26, "price_change": -0.16}}, + "source": "api", + "tool_name": None, + } + ], } } mock = AsyncMock(return_value=payload) diff --git a/tests/unit/test_subscription_event_model.py b/tests/unit/test_subscription_event_model.py new file mode 100644 index 0000000..efb58ae --- /dev/null +++ b/tests/unit/test_subscription_event_model.py @@ -0,0 +1,210 @@ +"""#149 -- ``SubscriptionEvent`` is typed from the event the API actually sends. + +Wire shape, ``WatchEvent#as_poll_json`` (oilpriceapi-api origin/main, +2026-09-13): ``id, seq, watch_id, observed_at, snapshot, deltas, source, +tool_name``. ``db/schema.rb`` has ``seq``, ``watch_id``, ``observed_at``, +``snapshot`` and ``deltas`` as ``null: false`` (``snapshot``/``deltas`` default +``{}``); ``source`` and ``tool_name`` are nullable. + +* ``snapshot`` is ``MarketBriefBuilder#snapshot_hash``: + ``{code: {price, change_24h_pct, currency, as_of}}``. ``change_24h_pct`` is nil + when there is no 24h comparison. +* ``deltas`` is ``Watch#compute_deltas``: ``{}`` on the first event, a code is + left out when either snapshot lacks a price, and ``pct_change`` is dropped + (``.compact``) when the prior price is 0. + +The model declared ``type``, ``code``, ``payload`` and ``created_at``, none of +which are sent, so every real event read ``None`` for them, while the fields +that are sent were untyped pydantic extras. + +The two events below are verbatim from ``GET /v1/subscriptions/events`` on +2026-09-13 (223 events on the test account; every one had all eight keys, and +only seq 1 had empty ``deltas``). Client tests drive the REAL sync and async +clients over a mocked transport. +""" + +import asyncio +import json +import warnings +from datetime import datetime, timezone +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from oilpriceapi import AsyncOilPriceAPI, OilPriceAPI, SubscriptionEvent +from oilpriceapi.exceptions import OilPriceAPIError + +# Not a credential: a fixture string. Every request here is mocked. +FIXTURE_KEY = "-".join(["fixture", "not", "a", "real", "key"]) + +MODES = ["sync", "async"] + +# live, seq 1: the first observation, so `deltas` is {}. +FIRST_EVENT = { + "id": "c167cbb1-e10f-48e3-a8c5-523ea5c9d2f9", + "seq": 1, + "watch_id": "b84b24a0-2b28-4eac-835e-db92bab5c0cb", + "observed_at": "2026-09-04T12:22:35Z", + "snapshot": {"BRENT_CRUDE_USD": {"as_of": "2026-09-04T12:21:56Z", "price": 94.76, "currency": "USD", "change_24h_pct": -2.16}}, + "deltas": {}, + "source": "api", + "tool_name": "opa_create_price_subscription", +} + +# live, seq 223. +LAST_EVENT = { + "id": "f5419cd4-86ea-47f8-b670-4770c9d68650", + "seq": 223, + "watch_id": "b84b24a0-2b28-4eac-835e-db92bab5c0cb", + "observed_at": "2026-09-13T20:32:18Z", + "snapshot": {"BRENT_CRUDE_USD": {"as_of": "2026-09-13T20:16:20Z", "price": 104.32, "currency": "USD", "change_24h_pct": 0.0}}, + "deltas": {"BRENT_CRUDE_USD": {"pct_change": -0.14, "price_change": -0.15}}, + "source": "api", + "tool_name": "opa_create_price_subscription", +} + + +def _response(payload): + response = Mock() + response.status_code = 200 + response.headers = {} + text = json.dumps(payload) + response.content = text.encode() + response.text = text + response.json.return_value = payload + return response + + +def _poll(mode, events, cursor): + payload = {"status": "success", "data": {"cursor": cursor, "has_more": False, "events": events}} + if mode == "sync": + client = OilPriceAPI(api_key=FIXTURE_KEY, max_retries=1) + with patch("httpx.Client.request", return_value=_response(payload)): + return client.subscriptions.events(since=0) + + async def go(): + client = AsyncOilPriceAPI(api_key=FIXTURE_KEY, max_retries=1) + with patch("httpx.AsyncClient.request", new=AsyncMock(return_value=_response(payload))): + return await client.subscriptions.events(since=0) + + return asyncio.run(go()) + + +def _without(event, key): + return {k: v for k, v in event.items() if k != key} + + +# --- the wire fields are typed --------------------------------------------------- + + +@pytest.mark.parametrize("mode", MODES) +def test_live_events_parse_into_typed_fields(mode): + page = _poll(mode, [FIRST_EVENT, LAST_EVENT], cursor=223) + first, last = page.events + + assert isinstance(last, SubscriptionEvent) + assert last.id == "f5419cd4-86ea-47f8-b670-4770c9d68650" + assert last.seq == 223 + assert last.watch_id == "b84b24a0-2b28-4eac-835e-db92bab5c0cb" + assert last.observed_at == datetime(2026, 9, 13, 20, 32, 18, tzinfo=timezone.utc) + assert last.source == "api" + assert last.tool_name == "opa_create_price_subscription" + + brent = last.snapshot["BRENT_CRUDE_USD"] + assert brent.price == 104.32 + assert brent.currency == "USD" + assert brent.change_24h_pct == 0.0 + assert brent.as_of == datetime(2026, 9, 13, 20, 16, 20, tzinfo=timezone.utc) + + delta = last.deltas["BRENT_CRUDE_USD"] + assert delta.price_change == -0.15 + assert delta.pct_change == -0.14 + + assert first.deltas == {} + assert first.snapshot["BRENT_CRUDE_USD"].change_24h_pct == -2.16 + + +def test_nothing_the_api_did_not_send_is_left_as_an_untyped_extra(): + event = SubscriptionEvent(**LAST_EVENT) + + assert event.model_extra == {} + + +# --- declared fields the API never sends are gone --------------------------------- + + +@pytest.mark.parametrize("name", ["type", "code", "payload"]) +def test_fields_the_api_never_sends_are_not_declared(name): + assert name not in SubscriptionEvent.model_fields + assert not hasattr(SubscriptionEvent(**LAST_EVENT), name) + + +def test_created_at_is_a_deprecated_alias_for_observed_at(): + event = SubscriptionEvent(**LAST_EVENT) + + with pytest.warns(DeprecationWarning, match="observed_at"): + value = event.created_at + + assert value == event.observed_at + assert "created_at" not in SubscriptionEvent.model_fields + + +# --- what the API can legitimately omit or null ------------------------------------ + + +def test_nullable_attribution_stays_none(): + event = SubscriptionEvent(**{**LAST_EVENT, "source": None, "tool_name": None}) + + assert event.source is None + assert event.tool_name is None + + +def test_null_24h_change_is_none_not_zero(): + snapshot = {"BRENT_CRUDE_USD": {"as_of": "2026-09-13T20:16:20Z", "price": 104.32, "currency": "USD", "change_24h_pct": None}} + + event = SubscriptionEvent(**{**LAST_EVENT, "snapshot": snapshot}) + + assert event.snapshot["BRENT_CRUDE_USD"].change_24h_pct is None + + +def test_pct_change_dropped_for_a_zero_prior_price_is_none_not_invented(): + event = SubscriptionEvent(**{**LAST_EVENT, "deltas": {"BRENT_CRUDE_USD": {"price_change": 104.32}}}) + + assert event.deltas["BRENT_CRUDE_USD"].price_change == 104.32 + assert event.deltas["BRENT_CRUDE_USD"].pct_change is None + + +def test_a_code_left_out_of_deltas_is_not_filled_in(): + event = SubscriptionEvent(**{**LAST_EVENT, "deltas": {}}) + + assert "BRENT_CRUDE_USD" not in event.deltas + + +# --- what the API always sends is required ---------------------------------------- + + +@pytest.mark.parametrize("mode", MODES) +@pytest.mark.parametrize("missing", ["id", "seq", "watch_id", "observed_at", "snapshot", "deltas"]) +def test_an_event_missing_a_field_the_api_always_sends_is_malformed(mode, missing): + with pytest.raises(OilPriceAPIError) as info: + _poll(mode, [_without(LAST_EVENT, missing)], cursor=223) + + assert info.value.code == "MALFORMED_RESPONSE" + assert missing in info.value.message + + +@pytest.mark.parametrize("mode", MODES) +@pytest.mark.parametrize("missing", ["price", "currency"]) +def test_a_snapshot_entry_missing_price_or_currency_is_malformed(mode, missing): + entry = _without(LAST_EVENT["snapshot"]["BRENT_CRUDE_USD"], missing) + + with pytest.raises(OilPriceAPIError) as info: + _poll(mode, [{**LAST_EVENT, "snapshot": {"BRENT_CRUDE_USD": entry}}], cursor=223) + + assert info.value.code == "MALFORMED_RESPONSE" + + +def test_no_deprecation_warning_from_parsing_alone(): + with warnings.catch_warnings(): + warnings.simplefilter("error") + SubscriptionEvent(**LAST_EVENT) diff --git a/tests/unit/test_subscriptions_list_events_strict.py b/tests/unit/test_subscriptions_list_events_strict.py index e5f3662..90f86ff 100644 --- a/tests/unit/test_subscriptions_list_events_strict.py +++ b/tests/unit/test_subscriptions_list_events_strict.py @@ -185,7 +185,9 @@ def test_list_live_body_parses(mode): "has_more missing": _page(cursor=41, events=[]), "has_more is a string": _page(cursor=41, has_more="false", events=[]), "cursor behind since": _page(cursor=0, has_more=False, events=[]), - "cursor behind the last event": _page(cursor=41, has_more=False, events=[{"seq": 43}]), + "cursor behind the last event": _page( + cursor=41, has_more=False, events=[{**WIRE_EVENTS_PAGE["data"]["events"][0], "seq": 43}] + ), } diff --git a/tests/unit/test_subscriptions_resource.py b/tests/unit/test_subscriptions_resource.py index 861ca61..430d210 100644 --- a/tests/unit/test_subscriptions_resource.py +++ b/tests/unit/test_subscriptions_resource.py @@ -21,6 +21,20 @@ ) +def _event(seq): + """An event in the live GET /v1/subscriptions/events shape (2026-09-13).""" + return { + "id": f"00000000-0000-4000-8000-{seq:012d}", + "seq": seq, + "watch_id": "abc-123", + "observed_at": "2026-09-08T16:20:42Z", + "snapshot": {"BRENT_CRUDE_USD": {"as_of": "2026-09-08T16:16:14Z", "price": 97.27, "currency": "USD", "change_24h_pct": -0.04}}, + "deltas": {"BRENT_CRUDE_USD": {"pct_change": -0.53, "price_change": -0.52}}, + "source": "api", + "tool_name": None, + } + + class TestIntervalMapping: """Friendly interval → interval_seconds conversion.""" @@ -161,10 +175,7 @@ def test_events(self, client): "data": { "cursor": 42, "has_more": True, - "events": [ - {"seq": 41, "watch_id": "abc-123", "type": "threshold", "code": "BRENT_CRUDE_USD"}, - {"seq": 42, "watch_id": "abc-123", "type": "threshold", "code": "BRENT_CRUDE_USD"}, - ], + "events": [_event(41), _event(42)], }, } with patch.object(client, "request", return_value=payload) as mock_req: From d9990f55799a16fa2a1ba51c6575f742c9882948 Mon Sep 17 00:00:00 2001 From: Karl Waldman Date: Sun, 13 Sep 2026 17:13:24 -0400 Subject: [PATCH 2/2] fix(models): keep never-sent SubscriptionEvent names as deprecated accessors (#149) 1.16.0 is a minor release, so type, code and payload are not removed. They become @property accessors that return None and emit DeprecationWarning naming the replacement (or that there is none), like created_at -> observed_at. They are not pydantic fields and are absent from model_dump(). All four are scheduled for removal in 2.0.0; CHANGELOG moves them under ### Deprecated. Tests: each accessor returns None/observed_at and warns on every access; none is serialized; parsing and polling all 223 live events (fixture captured 2026-09-13) emits no deprecation warning. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015ao5paex73xXvuM424Libo --- CHANGELOG.md | 22 +++-- oilpriceapi/models.py | 53 +++++++++++- .../subscription_events_live_2026-09-13.json | 1 + tests/unit/test_subscription_event_model.py | 86 ++++++++++++++++--- 4 files changed, 143 insertions(+), 19 deletions(-) create mode 100644 tests/unit/fixtures/subscription_events_live_2026-09-13.json diff --git a/CHANGELOG.md b/CHANGELOG.md index bcc9abe..04e171a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,11 +84,6 @@ All notable changes to the OilPriceAPI Python SDK will be documented in this fil to the new `SubscriptionEventDelta` (`price_change`, optional `pct_change`). An event missing a required field raises `OilPriceAPIError(code="MALFORMED_RESPONSE")`. - - **Behaviour change:** `type`, `code` and `payload` are removed, because no - field in the event corresponds to them. Code that read them only ever got - `None`. Watched codes are the keys of `event.snapshot`. - - `created_at` is kept as a deprecated property. It returns `observed_at` and - emits a `DeprecationWarning`. - **`error.code` is no longer set to a human sentence (#145).** For fail envelopes, `{"status": "fail", "data": {"error": ...}}`, the SDK copied `data.error` into `error.code` / `error.machine_code` whatever it held. Every @@ -137,6 +132,23 @@ All notable changes to the OilPriceAPI Python SDK will be documented in this fil default to `[]`, reading as a watch on nothing; the API always sends it, so a missing value now fails validation instead of being invented. +### Deprecated + +- **`SubscriptionEvent.type`, `.code`, `.payload` and `.created_at` are + deprecated and will be removed in 2.0.0 (#149).** The events API never sends + any of them. They are no longer pydantic fields and do not appear in + `model_dump()`. Each is now a property that emits a `DeprecationWarning` on + access: + - `type` returns `None` and has no equivalent, because every event is an + interval snapshot. + - `code` returns `None`. An event covers every watched code; use + `list(event.snapshot)`. + - `payload` returns `None`. Use `event.snapshot` and `event.deltas`. + - `created_at` returns `observed_at`, the event's timestamp, where it used to + return `None`. + + Parsing, polling and serializing events emit no warning. + ## [1.15.0] - 2026-09-13 ### Fixed diff --git a/oilpriceapi/models.py b/oilpriceapi/models.py index 4d1ed6f..d99a250 100644 --- a/oilpriceapi/models.py +++ b/oilpriceapi/models.py @@ -510,21 +510,68 @@ class SubscriptionEvent(BaseModel): source: Optional[str] = Field(default=None, description="Attribution source of the subscription") tool_name: Optional[str] = Field(default=None, description="Attribution tool name of the subscription") + # Deprecated accessors (#149). Plain properties, not pydantic fields, so they + # never appear in model_dump() or serialization. Removed in 2.0.0. + @property def created_at(self) -> datetime: - """Deprecated alias for ``observed_at``. + """Deprecated alias for ``observed_at``; removed in 2.0.0. The API never sent ``created_at`` on an event, so this always read ``None``. The event's timestamp is ``observed_at``. """ warnings.warn( - "SubscriptionEvent.created_at is deprecated and will be removed; use " - "observed_at. The events API does not send created_at (#149).", + "SubscriptionEvent.created_at is deprecated and will be removed in 2.0.0; " + "use observed_at. The events API does not send created_at (#149).", DeprecationWarning, stacklevel=2, ) return self.observed_at + @property + def type(self) -> None: + """Deprecated; always ``None``, removed in 2.0.0. + + The events API has no event type: every event is an interval snapshot. + """ + warnings.warn( + "SubscriptionEvent.type is deprecated and will be removed in 2.0.0. It was " + "always None: the events API sends no event type, and has no equivalent " + "field; every event is an interval snapshot (#149).", + DeprecationWarning, + stacklevel=2, + ) + return None + + @property + def code(self) -> None: + """Deprecated; always ``None``, removed in 2.0.0. + + An event can cover several codes: use ``snapshot.keys()``. + """ + warnings.warn( + "SubscriptionEvent.code is deprecated and will be removed in 2.0.0. It was " + "always None: an event covers every watched code, so use " + "list(event.snapshot) (#149).", + DeprecationWarning, + stacklevel=2, + ) + return None + + @property + def payload(self) -> None: + """Deprecated; always ``None``, removed in 2.0.0. + + The event data is in ``snapshot`` and ``deltas``. + """ + warnings.warn( + "SubscriptionEvent.payload is deprecated and will be removed in 2.0.0. It " + "was always None: use event.snapshot and event.deltas (#149).", + DeprecationWarning, + stacklevel=2, + ) + return None + class DataConnectorPrice(BaseModel): """Price from connected data source (BYOS - Bring Your Own Subscription).""" diff --git a/tests/unit/fixtures/subscription_events_live_2026-09-13.json b/tests/unit/fixtures/subscription_events_live_2026-09-13.json new file mode 100644 index 0000000..891c0f5 --- /dev/null +++ b/tests/unit/fixtures/subscription_events_live_2026-09-13.json @@ -0,0 +1 @@ +[{"id":"c167cbb1-e10f-48e3-a8c5-523ea5c9d2f9","seq":1,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T12:22:35Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T12:21:56Z","price":94.76,"currency":"USD","change_24h_pct":-2.16}},"deltas":{},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"1e4b5469-28c1-4366-b409-075129dbb898","seq":2,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T13:23:40Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T13:22:24Z","price":94.48,"currency":"USD","change_24h_pct":-1.74}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.3,"price_change":-0.28}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"67c14459-428f-49c8-8035-00240525769d","seq":3,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T14:24:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T14:21:19Z","price":93.86,"currency":"USD","change_24h_pct":-2.59}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.66,"price_change":-0.62}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"006dccca-0127-473d-a7bc-1a38dd679b67","seq":4,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T15:24:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T15:21:10Z","price":95.15,"currency":"USD","change_24h_pct":-2.02}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":1.37,"price_change":1.29}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"51d3e47b-7f4c-48ca-aafd-830b035f2e0d","seq":5,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T16:25:24Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T16:21:34Z","price":95.55,"currency":"USD","change_24h_pct":0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.42,"price_change":0.4}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"730ec41d-80cd-4163-8c33-58de66811fef","seq":6,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T17:26:33Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T17:21:11Z","price":95.62,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.07,"price_change":0.07}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4fd9d2c5-899b-45e8-9a87-7d0c368f4f84","seq":7,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T18:27:08Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T18:26:34Z","price":95.98,"currency":"USD","change_24h_pct":0.47}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.38,"price_change":0.36}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"89c7ecf5-a10c-45ea-9420-78e84b83e0d5","seq":8,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T19:27:30Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T19:26:24Z","price":96.2,"currency":"USD","change_24h_pct":0.6}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.23,"price_change":0.22}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"2fc536b3-270e-457d-b307-9770560a989e","seq":9,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T20:28:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T20:26:51Z","price":95.94,"currency":"USD","change_24h_pct":0.41}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.27,"price_change":-0.26}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"09eb40ce-e311-4229-ad7d-fa937ba09592","seq":10,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T21:29:21Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T21:26:18Z","price":95.76,"currency":"USD","change_24h_pct":-0.01}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.19,"price_change":-0.18}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"084af863-6dac-4599-ae5d-b8b3526df1a2","seq":11,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T22:30:29Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T22:20:40Z","price":95.85,"currency":"USD","change_24h_pct":-0.01}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.09,"price_change":0.09}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"6aa716c3-e224-4b94-8d93-645b27ad4c8b","seq":12,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-04T23:31:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-04T23:20:43Z","price":95.85,"currency":"USD","change_24h_pct":-0.07}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"844d0831-1d45-4613-9176-6fb5abfb56db","seq":13,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T00:32:07Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T00:25:40Z","price":95.85,"currency":"USD","change_24h_pct":0.03}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"ed8f625f-8e6b-4a82-9170-00f062e67bfa","seq":14,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T01:32:26Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T01:26:50Z","price":95.85,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"9aeb5fb8-46b1-4eac-a5da-3e42b4c7810c","seq":15,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T02:32:40Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T02:26:52Z","price":95.85,"currency":"USD","change_24h_pct":-0.09}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"992712a7-35a6-4fb1-97e6-2c728e8f9be9","seq":16,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T03:33:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T03:30:51Z","price":95.85,"currency":"USD","change_24h_pct":0.02}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"3641ea41-3650-4935-80c7-0b711d779987","seq":17,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T04:33:32Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T03:38:31Z","price":95.85,"currency":"USD","change_24h_pct":-0.24}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"ab7f6e43-cf0b-40eb-a1a3-8cd84a5d6c67","seq":18,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T05:33:35Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T05:30:58Z","price":95.85,"currency":"USD","change_24h_pct":0.32}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"02ad0920-ecd8-4cdf-9c0c-d617a9335364","seq":19,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T06:33:39Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T05:50:27Z","price":95.85,"currency":"USD","change_24h_pct":0.35}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"31894981-3cd5-41e0-b16b-d81bf76ed24d","seq":20,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T07:34:09Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T07:03:03Z","price":95.85,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"00fe66ec-c414-414a-ac0c-83c2f6611446","seq":21,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T08:34:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T08:14:38Z","price":95.85,"currency":"USD","change_24h_pct":0.78}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"eb57c2ca-519b-44ee-8ecd-948490799d01","seq":22,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T09:34:38Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T09:26:27Z","price":95.85,"currency":"USD","change_24h_pct":0.6}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"79ee6a6f-7eed-4271-a1d3-9576bc30eed7","seq":23,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T10:35:14Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T09:26:27Z","price":95.85,"currency":"USD","change_24h_pct":0.43}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"cd5d8c3a-5a39-4a27-b2b3-63a0a9bbab91","seq":24,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T11:36:36Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T11:06:50Z","price":95.85,"currency":"USD","change_24h_pct":0.86}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"06337a2b-57bb-4f7d-8495-8ad14b6a7195","seq":25,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T12:37:37Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T12:18:53Z","price":95.85,"currency":"USD","change_24h_pct":1.21}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"5bef0ea7-fdd7-4e38-82d8-c45b06bdcf13","seq":26,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T13:38:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T12:50:12Z","price":95.85,"currency":"USD","change_24h_pct":1.46}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"99e782b2-5e1d-486e-bd96-bcecebbed436","seq":27,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T14:39:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T14:02:25Z","price":95.85,"currency":"USD","change_24h_pct":1.72}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"980552a2-675a-4a4d-bec7-68e7c0402e46","seq":28,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T15:40:30Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T15:30:37Z","price":95.85,"currency":"USD","change_24h_pct":0.7}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"b0446ae1-34e6-4bd3-ac43-c97bacb3d2e8","seq":29,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T16:41:29Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T16:14:29Z","price":95.85,"currency":"USD","change_24h_pct":0.39}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"9b634ea3-ac60-403b-9d77-c72370342073","seq":30,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T17:42:35Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T17:30:41Z","price":95.85,"currency":"USD","change_24h_pct":0.32}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4794c256-4b43-4fe7-ab55-c66bc9b7ba99","seq":31,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T18:43:07Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T18:38:46Z","price":95.85,"currency":"USD","change_24h_pct":-0.13}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d5611a92-422f-4d94-9e33-5f881f6bc834","seq":32,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T19:43:12Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T18:43:17Z","price":95.85,"currency":"USD","change_24h_pct":-0.41}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7fdc9ab9-3593-4e1f-be03-591c8dd2301d","seq":33,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T20:43:27Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T20:30:53Z","price":95.85,"currency":"USD","change_24h_pct":-0.07}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"5826dd4d-0187-43b3-b06b-e37c9db8dc56","seq":34,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T21:44:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T21:02:26Z","price":95.85,"currency":"USD","change_24h_pct":0.02}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"425e8029-1abf-4f73-a3a1-39cc1e8caa62","seq":35,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T22:44:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T22:14:24Z","price":95.85,"currency":"USD","change_24h_pct":0.02}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"59c73025-0d12-4925-a668-4fd1b356abb2","seq":36,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-05T23:45:11Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-05T23:30:59Z","price":95.85,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"55429cf1-1d4b-42ae-b27d-4f722cd65848","seq":37,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T00:45:13Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T00:31:27Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.45,"price_change":0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c157b7fe-58cb-409f-98fc-72b4045c1c9e","seq":38,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T01:45:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T01:31:05Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a4ac13da-dd3e-4e99-8ded-c240af7e3fa0","seq":39,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T02:46:22Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T02:39:03Z","price":95.85,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.45,"price_change":-0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"b5c539cb-14d3-4a4f-9fd9-65d4543c5896","seq":40,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T03:47:12Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T03:46:23Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.45,"price_change":0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4e299bfb-edb1-4b74-9e59-3430f2ce9e41","seq":41,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T04:47:19Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T04:42:51Z","price":95.85,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.45,"price_change":-0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"f1beab7c-19fe-4b92-adc7-5b80b35014e6","seq":42,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T05:48:06Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T05:36:15Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.45,"price_change":0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c9202924-f6e7-48ca-9fc2-1c1ea6888b7a","seq":43,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T06:48:11Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T06:41:33Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7f153580-38f5-408c-806c-09fd9442ce51","seq":44,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T07:48:30Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T07:31:34Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"0bd2cc32-e994-40a8-8dfa-cec1ab0ef4e7","seq":45,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T08:49:33Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T08:46:32Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"57dabd58-51bc-4375-8ee5-435ac8d0097c","seq":46,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T09:50:14Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T09:41:06Z","price":96.28,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"41eb9b4c-f2a8-4c91-bbe2-5463b8f5bf44","seq":47,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T10:50:25Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T10:36:08Z","price":96.28,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7c3cfa19-31b7-4740-9db0-4c7f903614f1","seq":48,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T11:51:15Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T11:41:25Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"afdac8f9-07d4-41ab-b656-f5c4722e4d6e","seq":49,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T12:51:32Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T12:46:16Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"cb7bfc58-ef76-4aaa-b9d1-ba0d1d40fccf","seq":50,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T13:52:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T13:50:38Z","price":95.85,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.45,"price_change":-0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"8de2b9f3-65ef-4241-87e3-2dbc6093b85b","seq":51,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T14:52:31Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T14:51:16Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.45,"price_change":0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c5caacff-7e5c-49e1-a9a4-d54150ea444c","seq":52,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T15:53:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T15:46:09Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d1518570-ad00-4dfa-b0ea-9232793a43a6","seq":53,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T16:53:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T16:41:44Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"725f7d46-ca6e-4193-9bf7-7acd220c32a9","seq":54,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T17:54:13Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T17:36:20Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"43996e83-1eee-42da-91c4-d306129c9037","seq":55,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T18:54:22Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T18:46:32Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c6f284a8-9932-48d0-9a4a-fe29e7302956","seq":56,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T19:55:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T19:46:32Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c6565102-7d6f-4d6b-83a2-6faee722778f","seq":57,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T20:56:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T20:46:29Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"9a23bdee-d668-476f-897c-3897ae5dfa5a","seq":58,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T21:57:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T21:50:46Z","price":95.85,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.45,"price_change":-0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"3fa103bc-37ff-42ee-ad59-1a9cda049829","seq":59,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T22:58:20Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T22:51:14Z","price":96.28,"currency":"USD","change_24h_pct":1.05}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.45,"price_change":0.43}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"06265c3c-f3c6-4d12-b816-d5e76508172c","seq":60,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-06T23:59:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-06T23:50:18Z","price":96.85,"currency":"USD","change_24h_pct":0.59}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.59,"price_change":0.57}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4a9e582c-345a-49cc-8329-a24c2f00b762","seq":61,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T00:59:15Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T00:55:03Z","price":96.55,"currency":"USD","change_24h_pct":0.11}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.31,"price_change":-0.3}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"0dba597f-111a-458e-b5ed-d28e0bc30b3f","seq":62,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T02:00:03Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T01:54:26Z","price":96.64,"currency":"USD","change_24h_pct":0.42}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.09,"price_change":0.09}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"14aca387-5e69-472a-b0dc-96ccd9c30c78","seq":63,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T03:00:19Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T02:54:44Z","price":96.83,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.2,"price_change":0.19}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"ae1e0d76-3c4e-49cc-8627-f5a2152449a0","seq":64,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T04:01:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T03:56:13Z","price":96.28,"currency":"USD","change_24h_pct":1.03}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.57,"price_change":-0.55}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"0dac9dc5-7010-4e6a-83b2-0118f9410129","seq":65,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T05:01:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T04:54:58Z","price":97.18,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.93,"price_change":0.9}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a0a22c5a-ef79-40c7-9d9b-41b601b7cfe6","seq":66,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T06:02:34Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T05:56:33Z","price":96.28,"currency":"USD","change_24h_pct":0.45}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.93,"price_change":-0.9}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"65b36bcd-05a9-44f7-8b80-ff2dc1e0aaff","seq":67,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T07:03:35Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T07:02:32Z","price":97.38,"currency":"USD","change_24h_pct":1.27}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":1.14,"price_change":1.1}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"bc4d9370-12dc-4a10-8df3-110e5ecb24e3","seq":68,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T08:04:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T08:02:51Z","price":96.87,"currency":"USD","change_24h_pct":0.87}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.52,"price_change":-0.51}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"fb3d6e3f-50fa-4ef1-ab84-441851c60587","seq":69,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T09:05:01Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T09:02:08Z","price":96.69,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.19,"price_change":-0.18}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7215a807-8d50-431a-82b0-10f764951c66","seq":70,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T10:05:11Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T10:02:12Z","price":96.7,"currency":"USD","change_24h_pct":0.57}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.01,"price_change":0.01}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"3a71e456-5b0d-4aed-a21a-ef4b0c50fa82","seq":71,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T11:05:20Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T11:02:48Z","price":96.91,"currency":"USD","change_24h_pct":0.55}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.22,"price_change":0.21}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4c3a7d32-b2ce-4696-b26d-dd620c21440b","seq":72,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T12:06:11Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T12:05:15Z","price":97.55,"currency":"USD","change_24h_pct":1.17}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.66,"price_change":0.64}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"6e5aa743-2a86-44d0-bbcc-d7e724663531","seq":73,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T13:06:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T13:05:53Z","price":97.23,"currency":"USD","change_24h_pct":0.98}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.33,"price_change":-0.32}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"02e9e243-60ca-4d4d-ab5a-8e059b789b78","seq":74,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T14:07:30Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T14:06:13Z","price":97.42,"currency":"USD","change_24h_pct":1.16}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.2,"price_change":0.19}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"06e6bd30-bd7f-44c7-80ab-0ba1e770ee5b","seq":75,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T15:08:26Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T15:05:44Z","price":97.6,"currency":"USD","change_24h_pct":1.9}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.18,"price_change":0.18}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"ec0b012c-8a00-4aa5-a5f5-24157e7e79ff","seq":76,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T16:09:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T16:06:05Z","price":97.53,"currency":"USD","change_24h_pct":1.2}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.07,"price_change":-0.07}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"646829e0-d753-4002-a934-62677e03894d","seq":77,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T17:09:32Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T17:06:17Z","price":97.05,"currency":"USD","change_24h_pct":0.84}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.49,"price_change":-0.48}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"68dd0b3c-7e42-43b4-847a-1ff85695d307","seq":78,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T18:10:07Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T17:56:27Z","price":97.31,"currency":"USD","change_24h_pct":1.07}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.27,"price_change":0.26}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e143f0d4-617d-44ed-b4d5-e31597086392","seq":79,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T19:10:32Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T19:06:46Z","price":97.31,"currency":"USD","change_24h_pct":1.07}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"3dd70b19-f71b-4bf6-91f8-7e59fdc182e2","seq":80,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T20:10:36Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T20:06:30Z","price":96.28,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-1.06,"price_change":-1.03}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"fd1abb1e-dc04-4bcf-8f63-c864429ac35d","seq":81,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T21:11:07Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T21:01:15Z","price":96.28,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7996c792-b716-4c14-acc7-67eeba10f8c9","seq":82,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T22:11:09Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T21:51:30Z","price":96.28,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"3eed4a73-01b5-4bcc-9198-6680015b953d","seq":83,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-07T23:11:11Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-07T22:55:17Z","price":97.31,"currency":"USD","change_24h_pct":0.68}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":1.07,"price_change":1.03}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"dde3a635-762f-4f3f-a358-4d6b176a79c0","seq":84,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T00:11:39Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T00:07:25Z","price":97.01,"currency":"USD","change_24h_pct":0.57}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.31,"price_change":-0.3}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"bfc82db0-2ebe-44fb-b312-d54a5bdd6328","seq":85,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T01:12:06Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T01:11:28Z","price":97.05,"currency":"USD","change_24h_pct":0.46}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.04,"price_change":0.04}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"6ecc350e-94c9-44d3-9e42-92b2f6b20990","seq":86,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T02:12:39Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T02:11:13Z","price":97.03,"currency":"USD","change_24h_pct":0.47}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.02,"price_change":-0.02}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"13430d6c-022b-4f35-be67-a5b787c414fb","seq":87,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T03:13:24Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T03:12:51Z","price":97.38,"currency":"USD","change_24h_pct":0.32}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.36,"price_change":0.35}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"39ae41e2-5119-4822-8d7a-d8c06b78595d","seq":88,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T04:13:33Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T04:10:29Z","price":97.49,"currency":"USD","change_24h_pct":0.42}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.11,"price_change":0.11}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"49d2e19d-2664-4e80-94fe-aeda9e247d69","seq":89,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T05:14:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T05:11:17Z","price":97.62,"currency":"USD","change_24h_pct":0.43}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.13,"price_change":0.13}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c6ad7102-88c6-4b33-8320-24d21ab0dcd9","seq":90,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T06:15:07Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T06:14:17Z","price":98.37,"currency":"USD","change_24h_pct":0.69}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.77,"price_change":0.75}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"01954ab8-2e92-4b5b-9ed2-8e611606c602","seq":91,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T07:15:21Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T07:14:57Z","price":98.65,"currency":"USD","change_24h_pct":1.47}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.28,"price_change":0.28}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"09a200ea-a146-4e3a-93fc-f41787e8dc4d","seq":92,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T08:16:07Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T08:16:04Z","price":99.06,"currency":"USD","change_24h_pct":1.96}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.42,"price_change":0.41}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"ff101c1f-5001-4962-bc1d-0a25b33a80f0","seq":93,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T09:16:33Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T09:15:27Z","price":98.93,"currency":"USD","change_24h_pct":2.62}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.13,"price_change":-0.13}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"232a88fb-8f4f-4dee-82f9-08d3b12d51f3","seq":94,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T10:17:37Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T10:16:33Z","price":98.6,"currency":"USD","change_24h_pct":1.64}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.33,"price_change":-0.33}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"f2d22ffd-c6c6-473e-a27f-eadd34ad377e","seq":95,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T11:18:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T11:16:31Z","price":98.66,"currency":"USD","change_24h_pct":1.3}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.06,"price_change":0.06}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"96e0d474-708d-452f-8147-94a39e88ce09","seq":96,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T12:19:14Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T12:16:08Z","price":98.57,"currency":"USD","change_24h_pct":1.02}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.09,"price_change":-0.09}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"cd0de9ea-938c-4475-b9b2-ee72543f154d","seq":97,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T13:19:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T13:19:07Z","price":97.6,"currency":"USD","change_24h_pct":0.02}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.98,"price_change":-0.97}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"bf3f4870-2757-4764-a63c-847be1184fb5","seq":98,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T14:19:30Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T14:16:17Z","price":98.19,"currency":"USD","change_24h_pct":0.62}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.6,"price_change":0.59}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"8df7171a-d9b6-4196-adfd-dec93e6fcfea","seq":99,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T15:20:09Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T15:19:14Z","price":97.79,"currency":"USD","change_24h_pct":-0.06}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.41,"price_change":-0.4}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d73c7b25-875b-403e-a429-0d7049245c30","seq":100,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T16:20:42Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T16:16:14Z","price":97.27,"currency":"USD","change_24h_pct":-0.04}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.53,"price_change":-0.52}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a835a930-f34f-4001-80b1-38fb3cde3797","seq":101,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T17:21:19Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T17:20:37Z","price":97.02,"currency":"USD","change_24h_pct":-0.08}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.26,"price_change":-0.25}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"0b9caceb-b4ed-4903-9455-9ea6401a39c4","seq":102,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T18:22:19Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T18:20:32Z","price":97.23,"currency":"USD","change_24h_pct":0.3}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.22,"price_change":0.21}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e470c687-c5c4-4f47-8f2f-3899759137d8","seq":103,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T19:23:11Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T19:21:09Z","price":98.55,"currency":"USD","change_24h_pct":1.37}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":1.36,"price_change":1.32}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"cb2bb63a-362f-4f26-a34f-46b1f5dbf66f","seq":104,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T20:23:27Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T20:21:31Z","price":99.16,"currency":"USD","change_24h_pct":1.63}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.62,"price_change":0.61}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c9817180-abd7-418f-a97c-a0966c9295af","seq":105,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T21:24:15Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T21:21:05Z","price":99.28,"currency":"USD","change_24h_pct":2.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.12,"price_change":0.12}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d6abf791-0e2c-4103-b927-75e1a4eb285f","seq":106,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T22:24:35Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T22:19:09Z","price":99.39,"currency":"USD","change_24h_pct":2.2}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.11,"price_change":0.11}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e71c2bce-3399-4ddc-a118-089d26297e46","seq":107,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-08T23:25:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-08T23:20:13Z","price":99.39,"currency":"USD","change_24h_pct":2.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"5fb5b325-021e-4ce7-bbba-9cc7b8ee5501","seq":108,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T00:26:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T00:25:21Z","price":99.32,"currency":"USD","change_24h_pct":2.33}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.07,"price_change":-0.07}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"b9b268e0-6d6e-45fe-9d27-591139d58918","seq":109,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T01:26:12Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T01:22:10Z","price":99.29,"currency":"USD","change_24h_pct":2.48}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.03,"price_change":-0.03}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"61413192-8252-4f3b-8c56-95c8106b034a","seq":110,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T02:26:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T02:25:39Z","price":99.38,"currency":"USD","change_24h_pct":2.4}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.09,"price_change":0.09}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"2029aaee-5459-4b60-8940-ea13e6285e88","seq":111,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T03:26:37Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T03:25:45Z","price":99.6,"currency":"USD","change_24h_pct":2.33}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.22,"price_change":0.22}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"bd79db94-4797-473e-a1b8-71d91675b229","seq":112,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T04:27:34Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T04:26:17Z","price":99.27,"currency":"USD","change_24h_pct":1.89}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.33,"price_change":-0.33}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"9cb47a08-121b-42a7-874b-91bec98f2540","seq":113,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T05:28:33Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T05:26:11Z","price":99.02,"currency":"USD","change_24h_pct":1.4}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.25,"price_change":-0.25}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7cac6f82-7f73-4e31-8736-c87ff86f95e3","seq":114,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T06:29:03Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T06:26:25Z","price":99.23,"currency":"USD","change_24h_pct":0.7}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.21,"price_change":0.21}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"13c252bb-62bb-4694-a3c1-46ea838989db","seq":115,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T07:29:26Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T07:26:23Z","price":99.84,"currency":"USD","change_24h_pct":1.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.61,"price_change":0.61}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e3b4b53c-0e91-4d7d-af2e-80e2a9255d3b","seq":116,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T08:30:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T08:26:23Z","price":99.72,"currency":"USD","change_24h_pct":0.7}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.12,"price_change":-0.12}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"342e2048-8532-411c-9371-6adc635333aa","seq":117,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T09:31:30Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T09:30:14Z","price":100.46,"currency":"USD","change_24h_pct":0.99}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.74,"price_change":0.74}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"cd574a00-43c0-49b8-9c44-bf57247e0c0d","seq":118,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T10:32:33Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T10:31:18Z","price":100.46,"currency":"USD","change_24h_pct":1.9}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"3a2dca2c-af30-444e-a3c2-4c53a3c7120e","seq":119,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T11:33:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T11:31:37Z","price":100.82,"currency":"USD","change_24h_pct":2.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.36,"price_change":0.36}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a83461c3-c578-4628-890e-5b10afc10ea6","seq":120,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T12:33:34Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T12:32:15Z","price":100.88,"currency":"USD","change_24h_pct":2.32}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.06,"price_change":0.06}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"f71737c0-e1e2-4907-a972-5aa97b3fdca4","seq":121,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T13:34:20Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T13:31:17Z","price":100.63,"currency":"USD","change_24h_pct":3.18}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.25,"price_change":-0.25}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"619bb917-c58a-42e7-8bb5-ec0363909a27","seq":122,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T14:35:20Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T14:31:16Z","price":101.24,"currency":"USD","change_24h_pct":2.75}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.61,"price_change":0.61}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4cb55c1b-6817-41df-b378-8e643ade4c67","seq":123,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T15:36:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T15:35:29Z","price":101.39,"currency":"USD","change_24h_pct":3.47}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.15,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d43c5c57-7e66-4910-812d-a7792345b32d","seq":124,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T16:37:02Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T16:36:18Z","price":101.08,"currency":"USD","change_24h_pct":3.93}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.31,"price_change":-0.31}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"b56c1541-9915-4ace-941e-37176c08c026","seq":125,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T17:37:25Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T17:36:17Z","price":100.51,"currency":"USD","change_24h_pct":3.6}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.56,"price_change":-0.57}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7149a965-2bf8-443f-90e1-1e7b1066e5cf","seq":126,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T18:37:46Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T18:36:27Z","price":101.06,"currency":"USD","change_24h_pct":3.31}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.55,"price_change":0.55}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"aa059797-0106-4bd4-be03-3b1c681dd938","seq":127,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T19:38:08Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T19:36:07Z","price":101.27,"currency":"USD","change_24h_pct":2.72}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.21,"price_change":0.21}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"577a087e-b6c6-4c45-b74d-f52c6af11a4d","seq":128,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T20:39:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T20:36:17Z","price":101.69,"currency":"USD","change_24h_pct":2.6}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.41,"price_change":0.42}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"b6efaeac-a5a8-473b-8c5a-95461c1482e1","seq":129,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T21:39:26Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T21:38:16Z","price":101.74,"currency":"USD","change_24h_pct":2.35}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.05,"price_change":0.05}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"9068fe52-fe18-42a6-b197-826e2e4bfcb0","seq":130,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T22:40:05Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T22:36:18Z","price":101.93,"currency":"USD","change_24h_pct":2.65}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.19,"price_change":0.19}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"b9a0a3d4-73cb-4621-b576-01a709bfa1bc","seq":131,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-09T23:40:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-09T23:15:23Z","price":101.75,"currency":"USD","change_24h_pct":2.37}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.18,"price_change":-0.18}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"25021a8c-1117-4ab4-9246-a9deef54ae10","seq":132,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T00:40:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T00:38:49Z","price":101.4,"currency":"USD","change_24h_pct":1.87}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.34,"price_change":-0.35}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"db557304-e189-4dad-9708-118a222211b5","seq":133,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T01:41:22Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T01:40:41Z","price":101.58,"currency":"USD","change_24h_pct":2.17}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.18,"price_change":0.18}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"9bc02e5b-06d4-4f9e-9db5-70f4f50fe907","seq":134,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T02:42:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T02:41:13Z","price":101.33,"currency":"USD","change_24h_pct":1.9}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.25,"price_change":-0.25}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"65a9b82f-51a7-4751-8798-1471791d7561","seq":135,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T03:43:07Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T03:42:20Z","price":101.04,"currency":"USD","change_24h_pct":1.34}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.29,"price_change":-0.29}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c0a141a4-c2c9-4ef1-9a0b-58551fce6097","seq":136,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T04:43:44Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T04:41:26Z","price":101.05,"currency":"USD","change_24h_pct":1.94}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.01,"price_change":0.01}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"971606b6-c902-4bb7-acac-e1946da3b79d","seq":137,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T05:44:05Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T05:42:39Z","price":100.59,"currency":"USD","change_24h_pct":1.97}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.46,"price_change":-0.46}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e118738c-a5e7-4653-acf1-0f64bbc10a8a","seq":138,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T06:44:42Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T06:43:16Z","price":100.47,"currency":"USD","change_24h_pct":1.08}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.12,"price_change":-0.12}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"01fe32c3-82a2-49ef-8980-4206406f031c","seq":139,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T07:45:31Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T07:41:40Z","price":101.4,"currency":"USD","change_24h_pct":0.85}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.93,"price_change":0.93}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"3eb322db-ade0-4168-84ac-945b2f30d20f","seq":140,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T08:46:07Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T08:45:24Z","price":102.04,"currency":"USD","change_24h_pct":2.01}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.63,"price_change":0.64}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d14cf8c3-0e20-45d2-b24a-f33cfaebf774","seq":141,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T09:46:21Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T09:46:10Z","price":102.59,"currency":"USD","change_24h_pct":1.61}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.54,"price_change":0.55}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"fb1b88f6-097f-46c4-b246-f2f5346084da","seq":142,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T10:46:31Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T10:45:33Z","price":101.86,"currency":"USD","change_24h_pct":1.28}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.71,"price_change":-0.73}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"0c8579f0-4942-4ba7-8cc5-51155611b143","seq":143,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T11:47:03Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T11:46:08Z","price":102.0,"currency":"USD","change_24h_pct":1.49}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.14}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e0ce1a8a-c549-443a-bda5-f244aef5f9a4","seq":144,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T12:47:20Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T12:46:10Z","price":105.29,"currency":"USD","change_24h_pct":3.29}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":3.23,"price_change":3.29}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"f152b6b3-3b59-4ca6-bc96-fe8145d71ec2","seq":145,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T13:48:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T13:46:11Z","price":104.79,"currency":"USD","change_24h_pct":4.39}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.47,"price_change":-0.5}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"74e19516-5751-46a5-8596-55bfea650844","seq":146,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T14:49:05Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T14:46:09Z","price":104.68,"currency":"USD","change_24h_pct":2.95}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.1,"price_change":-0.11}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"14616bde-0912-4d16-b3c5-0c021f67b6ec","seq":147,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T15:49:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T15:46:25Z","price":105.95,"currency":"USD","change_24h_pct":4.34}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":1.21,"price_change":1.27}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e92bcb7e-3b79-4081-94bf-9730f0385566","seq":148,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T16:49:20Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T16:46:32Z","price":107.16,"currency":"USD","change_24h_pct":6.1}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":1.14,"price_change":1.21}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"67918d1e-df5c-464e-899a-9736b44720a0","seq":149,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T17:50:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T17:46:30Z","price":107.38,"currency":"USD","change_24h_pct":6.39}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.21,"price_change":0.22}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7c582b04-db14-4fed-8147-fc98b482fdaf","seq":150,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T18:51:38Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T18:50:56Z","price":107.22,"currency":"USD","change_24h_pct":5.97}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.15,"price_change":-0.16}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a4d950a1-165a-4325-9224-2c43f901cb16","seq":151,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T19:52:12Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T19:51:31Z","price":107.92,"currency":"USD","change_24h_pct":6.59}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.65,"price_change":0.7}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"1392e087-bb87-42e1-b5d3-a05c508caade","seq":152,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T20:53:15Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T20:51:19Z","price":108.75,"currency":"USD","change_24h_pct":7.16}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.77,"price_change":0.83}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"89590ef7-6f1b-497b-b96e-33005b883512","seq":153,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T21:53:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T21:51:14Z","price":109.12,"currency":"USD","change_24h_pct":7.1}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.34,"price_change":0.37}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d5cebe02-1253-4f13-bc9a-2c918282861a","seq":154,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T22:54:20Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T22:51:05Z","price":109.14,"currency":"USD","change_24h_pct":7.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.02,"price_change":0.02}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"54c5902f-df7a-42cc-8aae-252fa6948772","seq":155,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-10T23:55:25Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-10T23:43:14Z","price":109.29,"currency":"USD","change_24h_pct":7.41}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"1f38c053-0133-4cae-bc92-b91fb485355a","seq":156,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T00:55:32Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T00:51:48Z","price":108.5,"currency":"USD","change_24h_pct":6.87}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.72,"price_change":-0.79}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7fe03333-6946-43ec-8e86-44c230ac0c3c","seq":157,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T01:56:09Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T01:54:54Z","price":107.81,"currency":"USD","change_24h_pct":5.67}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.64,"price_change":-0.69}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"83274ac2-de18-4772-878b-5ce0a2108679","seq":158,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T02:57:19Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T02:56:27Z","price":107.85,"currency":"USD","change_24h_pct":6.41}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.04,"price_change":0.04}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"6e7f5f76-76bc-41eb-9bd0-74020c45d8e1","seq":159,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T03:58:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T03:56:16Z","price":108.42,"currency":"USD","change_24h_pct":7.28}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.53,"price_change":0.57}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"ee059c84-2fed-4e3d-ae57-ce895b2e1de2","seq":160,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T04:59:32Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T04:56:17Z","price":107.87,"currency":"USD","change_24h_pct":6.7}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.51,"price_change":-0.55}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"682d5271-de45-4d24-9749-a5f41c76d405","seq":161,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T05:59:42Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T05:56:20Z","price":105.96,"currency":"USD","change_24h_pct":5.54}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-1.77,"price_change":-1.91}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"96df458c-d842-4c40-a3c4-3c9a91cd70fd","seq":162,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T07:00:13Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T06:56:25Z","price":106.02,"currency":"USD","change_24h_pct":5.54}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.06,"price_change":0.06}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"b217e3e7-5073-4337-8d33-dbf3564b18de","seq":163,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T08:01:05Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T07:56:18Z","price":105.75,"currency":"USD","change_24h_pct":4.68}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.25,"price_change":-0.27}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"2100cec9-6662-46c6-8142-ea05b6a19c14","seq":164,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T09:01:20Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T09:00:58Z","price":104.14,"currency":"USD","change_24h_pct":2.78}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-1.52,"price_change":-1.61}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"af380f06-109b-4e9e-a466-3738e1e334f6","seq":165,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T10:02:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T10:01:10Z","price":104.12,"currency":"USD","change_24h_pct":1.67}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.02,"price_change":-0.02}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e1627e37-87f0-49db-a0fc-8ae10e818865","seq":166,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T11:02:17Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T11:01:11Z","price":103.53,"currency":"USD","change_24h_pct":2.01}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.57,"price_change":-0.59}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"153f1054-29ae-46fe-8e5d-8f15a61ae136","seq":167,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T12:03:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T12:02:16Z","price":104.01,"currency":"USD","change_24h_pct":2.37}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.46,"price_change":0.48}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"69ed7e90-4b95-4060-a9f5-8fd2b3d01a1f","seq":168,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T13:03:24Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T13:02:43Z","price":104.45,"currency":"USD","change_24h_pct":-1.38}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.42,"price_change":0.44}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"ec362ab5-fbc4-4477-ba1f-a980612df786","seq":169,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T14:03:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T14:02:20Z","price":104.31,"currency":"USD","change_24h_pct":-0.35}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.13,"price_change":-0.14}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"b3171118-52fb-474c-90a5-fd7a7966b3e1","seq":170,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T15:03:35Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T15:02:37Z","price":104.42,"currency":"USD","change_24h_pct":0.33}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.11,"price_change":0.11}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"eb2977f5-d92e-4758-b4c0-942a45d0ae60","seq":171,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T16:04:25Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T16:02:21Z","price":104.76,"currency":"USD","change_24h_pct":-1.57}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.33,"price_change":0.34}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e79e7f13-745c-426c-8fdc-61aebffdb9fa","seq":172,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T17:04:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T17:02:20Z","price":105.02,"currency":"USD","change_24h_pct":-1.76}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.25,"price_change":0.26}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c8377d91-a736-4020-b3f0-6634a5f0af2c","seq":173,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T18:04:37Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T18:02:40Z","price":104.6,"currency":"USD","change_24h_pct":-2.92}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.4,"price_change":-0.42}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"59825d55-e480-40eb-b2a8-97d93e88647b","seq":174,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T19:05:01Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T19:02:51Z","price":104.94,"currency":"USD","change_24h_pct":-2.69}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.33,"price_change":0.34}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"618c66df-9a81-4501-ac98-11512f6859e1","seq":175,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T20:05:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T20:02:31Z","price":104.88,"currency":"USD","change_24h_pct":-2.88}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.06,"price_change":-0.06}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"250ca3d8-d5f6-46fb-8a04-ca81f665b5fc","seq":176,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T21:06:13Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T21:05:26Z","price":104.52,"currency":"USD","change_24h_pct":-4.05}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.34,"price_change":-0.36}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"54a45445-bc16-4aaa-a802-355d2e5b01ae","seq":177,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T22:07:06Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T22:06:15Z","price":103.81,"currency":"USD","change_24h_pct":-4.89}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.68,"price_change":-0.71}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"69a41cce-9370-4d35-9353-4d0d4b1fa704","seq":178,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-11T23:07:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T22:51:16Z","price":104.32,"currency":"USD","change_24h_pct":-4.31}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.49,"price_change":0.51}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"1b330b55-7ff7-44aa-bb6a-49eaa637abf9","seq":179,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T00:07:24Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-11T23:51:07Z","price":104.47,"currency":"USD","change_24h_pct":-4.98}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"aeffd30d-0730-45dd-9a5e-04832c979fc8","seq":180,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T01:08:05Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T01:01:16Z","price":104.47,"currency":"USD","change_24h_pct":-3.73}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"5cda00f2-a9cc-4b83-96cb-c71629ee6115","seq":181,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T02:08:15Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T01:26:10Z","price":104.32,"currency":"USD","change_24h_pct":-3.29}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.14,"price_change":-0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a8fa7e82-8a52-4ba5-9be1-bd2a826b7500","seq":182,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T03:08:27Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T02:38:20Z","price":104.32,"currency":"USD","change_24h_pct":-3.35}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4d3462ce-25a0-4045-8cdc-35bba648b722","seq":183,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T04:09:11Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T03:50:18Z","price":104.32,"currency":"USD","change_24h_pct":-3.94}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"228849c2-4d44-4a5b-8d38-f41623148b56","seq":184,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T05:09:14Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T05:02:51Z","price":104.32,"currency":"USD","change_24h_pct":-2.96}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"69c22125-b4c6-4f25-b4ad-e2ed0ff6fb03","seq":185,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T06:10:28Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T05:02:51Z","price":104.32,"currency":"USD","change_24h_pct":-1.31}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"7393ad8d-2d4e-40e4-bc40-08dced7dfa2b","seq":186,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T07:11:32Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T06:18:50Z","price":104.47,"currency":"USD","change_24h_pct":-1.41}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"e04b8411-a2e2-4bc0-8be0-d36869d87d63","seq":187,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T08:11:35Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T07:26:24Z","price":104.32,"currency":"USD","change_24h_pct":-1.5}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.14,"price_change":-0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"5280c9f9-9006-48fd-aac2-b82859ff61ff","seq":188,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T09:12:08Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T08:54:56Z","price":104.47,"currency":"USD","change_24h_pct":-0.02}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"81f1ab8f-0d87-421c-82c8-385bf96e2c3c","seq":189,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T10:12:22Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T09:26:41Z","price":104.32,"currency":"USD","change_24h_pct":0.27}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.14,"price_change":-0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d9d49a97-66f5-4531-b31d-7a0cbfb65ecd","seq":190,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T11:12:31Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T10:38:21Z","price":104.32,"currency":"USD","change_24h_pct":0.72}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4db02945-d64f-41ae-9789-19d7e30f5c98","seq":191,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T12:13:03Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T12:06:56Z","price":104.47,"currency":"USD","change_24h_pct":0.26}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a8eb9141-129a-43cb-b18e-ca8eca08da47","seq":192,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T13:13:36Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T13:07:16Z","price":104.47,"currency":"USD","change_24h_pct":-0.28}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"55adbaeb-e8ca-4681-bd74-2087d47123bb","seq":193,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T14:14:34Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T13:50:58Z","price":104.32,"currency":"USD","change_24h_pct":-0.11}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.14,"price_change":-0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"6c62730f-c55a-43cf-b67a-2a3baea0874e","seq":194,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T15:15:05Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T15:02:19Z","price":104.32,"currency":"USD","change_24h_pct":-0.26}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"953c37c7-1621-4486-bf5e-80c9a6e1a7b1","seq":195,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T16:15:22Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T16:02:30Z","price":104.32,"currency":"USD","change_24h_pct":-0.46}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"de597156-19a9-4069-a4d8-9c3a8dd57946","seq":196,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T17:16:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T17:14:32Z","price":104.32,"currency":"USD","change_24h_pct":-0.74}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"3c2d4d4e-da8c-4c71-98d0-31e9259b612e","seq":197,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T18:17:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T17:14:32Z","price":104.32,"currency":"USD","change_24h_pct":-0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"cd3476ba-2ae4-4640-846b-21314323785e","seq":198,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T19:18:15Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T18:42:38Z","price":104.47,"currency":"USD","change_24h_pct":-0.43}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"bfde318b-2a1e-4b00-a30e-48919ca99bd5","seq":199,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T20:18:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T19:42:58Z","price":104.47,"currency":"USD","change_24h_pct":-0.41}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"cf601a77-b705-4cd8-b4cb-f9d133a43543","seq":200,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T21:19:19Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T20:42:59Z","price":104.47,"currency":"USD","change_24h_pct":-0.05}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"f57c9534-2051-4a5f-8032-1d3a4ee1540a","seq":201,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T22:20:01Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T21:54:30Z","price":104.47,"currency":"USD","change_24h_pct":0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"43da9fcb-1685-40cb-a19d-2e45028bf23d","seq":202,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-12T23:20:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-12T23:07:01Z","price":104.47,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c2bb9c89-48b1-4727-b3b4-63c5399c7a43","seq":203,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T00:21:38Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T00:16:20Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.14,"price_change":-0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"c98bce1e-9a25-44d2-b3c3-88e06270e686","seq":204,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T01:22:12Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T01:16:21Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"f8c3939f-19c7-4cbe-b7be-43f535a9698d","seq":205,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T02:23:06Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T02:16:09Z","price":104.32,"currency":"USD","change_24h_pct":-0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"0614649a-283a-4e24-8ae3-b4713168f404","seq":206,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T03:23:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T03:21:21Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"38d39f85-2954-4253-b273-cd50bfbb8026","seq":207,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T04:23:38Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T04:21:19Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"1d3f0c71-016d-4a82-aa8d-e9c17f5da545","seq":208,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T05:24:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T05:21:05Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"d5f609ac-9347-46ea-b530-1387536e4963","seq":209,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T06:25:35Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T06:11:18Z","price":104.32,"currency":"USD","change_24h_pct":-0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"15c66be5-0fe9-468f-9322-2b2ccacc62a5","seq":210,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T07:26:05Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T07:16:38Z","price":104.32,"currency":"USD","change_24h_pct":-0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a972214f-e294-4582-bc8d-8063523b8455","seq":211,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T08:26:10Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T08:11:37Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"f016a50b-ec3f-4329-8666-07a02dced0ee","seq":212,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T09:26:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T09:06:28Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"11ead267-d0c5-4cf2-ae36-df9785e65455","seq":213,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T10:26:27Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T10:21:44Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"a21b1a63-5fb8-4f88-93d2-3dc25e5794e1","seq":214,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T11:26:32Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T11:16:45Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"9964bb98-a459-4ebb-9070-93ebca07f8ff","seq":215,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T12:27:24Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T12:26:20Z","price":104.32,"currency":"USD","change_24h_pct":-0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"38aa6c3e-5eb4-4aee-88cc-c56c8867d896","seq":216,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T13:28:08Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T13:21:22Z","price":104.32,"currency":"USD","change_24h_pct":-0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"bc405641-8689-4db6-b73c-2615a6374154","seq":217,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T14:29:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T14:16:23Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"5500adfd-088a-48d8-8e5e-d62932222eb1","seq":218,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T15:30:16Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T15:26:42Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"71edf3d1-7028-4b6e-b611-4d9d094f9e43","seq":219,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T16:31:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T16:30:34Z","price":104.47,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"1beefcae-b005-4936-9982-b5240d0679c9","seq":220,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T17:31:23Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T17:21:14Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.14,"price_change":-0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"0edd2b06-9684-49da-af87-8f9891481e0c","seq":221,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T18:31:26Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T18:21:18Z","price":104.32,"currency":"USD","change_24h_pct":0.14}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.0,"price_change":0.0}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"4fe10103-e100-420b-a5d5-c077bde5c159","seq":222,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T19:32:06Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T19:30:36Z","price":104.47,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":0.14,"price_change":0.15}},"source":"api","tool_name":"opa_create_price_subscription"},{"id":"f5419cd4-86ea-47f8-b670-4770c9d68650","seq":223,"watch_id":"b84b24a0-2b28-4eac-835e-db92bab5c0cb","observed_at":"2026-09-13T20:32:18Z","snapshot":{"BRENT_CRUDE_USD":{"as_of":"2026-09-13T20:16:20Z","price":104.32,"currency":"USD","change_24h_pct":0.0}},"deltas":{"BRENT_CRUDE_USD":{"pct_change":-0.14,"price_change":-0.15}},"source":"api","tool_name":"opa_create_price_subscription"}] diff --git a/tests/unit/test_subscription_event_model.py b/tests/unit/test_subscription_event_model.py index efb58ae..2721e73 100644 --- a/tests/unit/test_subscription_event_model.py +++ b/tests/unit/test_subscription_event_model.py @@ -27,6 +27,7 @@ import json import warnings from datetime import datetime, timezone +from pathlib import Path from unittest.mock import AsyncMock, Mock, patch import pytest @@ -130,23 +131,56 @@ def test_nothing_the_api_did_not_send_is_left_as_an_untyped_extra(): assert event.model_extra == {} -# --- declared fields the API never sends are gone --------------------------------- +# --- names the API never sends: deprecated accessors, not fields -------------------- +DEPRECATED = ["type", "code", "payload", "created_at"] -@pytest.mark.parametrize("name", ["type", "code", "payload"]) -def test_fields_the_api_never_sends_are_not_declared(name): - assert name not in SubscriptionEvent.model_fields - assert not hasattr(SubscriptionEvent(**LAST_EVENT), name) + +@pytest.mark.parametrize( + ("name", "guidance"), + [("type", "no equivalent"), ("code", "event.snapshot"), ("payload", "event.snapshot")], +) +def test_never_sent_names_read_none_and_warn_once_per_access(name, guidance): + event = SubscriptionEvent(**LAST_EVENT) + + for _ in range(2): + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + value = getattr(event, name) + assert value is None + assert [w.category for w in caught] == [DeprecationWarning] + message = str(caught[0].message) + assert f"SubscriptionEvent.{name} is deprecated" in message + assert "2.0.0" in message + assert guidance in message def test_created_at_is_a_deprecated_alias_for_observed_at(): event = SubscriptionEvent(**LAST_EVENT) - with pytest.warns(DeprecationWarning, match="observed_at"): - value = event.created_at + for _ in range(2): + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + value = event.created_at + assert value == event.observed_at + assert [w.category for w in caught] == [DeprecationWarning] + assert "use observed_at" in str(caught[0].message) + assert "2.0.0" in str(caught[0].message) + + +@pytest.mark.parametrize("name", DEPRECATED) +def test_deprecated_names_are_not_fields_and_not_serialized(name): + event = SubscriptionEvent(**LAST_EVENT) + + with warnings.catch_warnings(): + warnings.simplefilter("error") + dumped = event.model_dump() + dumped_json = json.loads(event.model_dump_json()) - assert value == event.observed_at - assert "created_at" not in SubscriptionEvent.model_fields + assert name not in SubscriptionEvent.model_fields + assert name not in dumped + assert name not in dumped_json + assert set(dumped) == set(LAST_EVENT) # --- what the API can legitimately omit or null ------------------------------------ @@ -204,7 +238,37 @@ def test_a_snapshot_entry_missing_price_or_currency_is_malformed(mode, missing): assert info.value.code == "MALFORMED_RESPONSE" -def test_no_deprecation_warning_from_parsing_alone(): +LIVE_EVENTS = json.loads( + (Path(__file__).parent / "fixtures" / "subscription_events_live_2026-09-13.json").read_text() +) + + +def test_parsing_all_223_live_events_emits_no_warning_and_leaves_nothing_untyped(): + assert len(LIVE_EVENTS) == 223 + with warnings.catch_warnings(): warnings.simplefilter("error") - SubscriptionEvent(**LAST_EVENT) + events = [SubscriptionEvent(**raw) for raw in LIVE_EVENTS] + for event in events: + event.model_dump() + + assert [event.seq for event in events] == list(range(1, 224)) + assert all(event.model_extra == {} for event in events) + assert [event.seq for event in events if not event.deltas] == [1] + + +@pytest.mark.parametrize("mode", MODES) +def test_polling_the_live_events_emits_no_deprecation_warning(mode): + # Only deprecation / SubscriptionEvent warnings are this PR's concern: building + # a client can emit unrelated ImportWarnings for optional extras. + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + page = _poll(mode, LIVE_EVENTS, cursor=223) + + assert len(page) == 223 + ours = [ + str(w.message) + for w in caught + if issubclass(w.category, DeprecationWarning) or "SubscriptionEvent" in str(w.message) + ] + assert ours == []