From 52c0ab583c469843efc6f71abe5f842b6c4a2380 Mon Sep 17 00:00:00 2001 From: Chip Kent <5250374+chipkent@users.noreply.github.com> Date: Fri, 23 Oct 2026 17:07:56 -0600 Subject: [PATCH 1/2] feat: add list_funding_conditions for /fed/v1/funding-conditions The Fed funding-conditions endpoint is documented in the OpenAPI spec and on massive.com/docs but had no client method; the economy client only covered treasury-yields, inflation, inflation-expectations, and labor-market. Add EconomyClient.list_funding_conditions, a paginated list method in the same shape as list_treasury_yields. Its query parameters follow the documented ones exactly: date, date.gt, date.gte, date.lt, date.lte, limit, and sort (this endpoint documents no date.any_of or order). sort is typed Optional[str], as in list_labor_market_indicators, rather than Union[str, Sort]. The endpoint takes column.direction values such as "date.desc", so Sort.ASC would send a bare "asc" that it does not accept. Add the FedFundingConditions model with all 22 documented result fields (date plus the fed funds, IORB, SOFR, OBFR, TGCR, repo/reverse repo, and commercial paper series). Tests cover a two-page fixture (including a weekend row whose business-day series are null), check with pook that all seven parameters are sent under their API names (date.gt rather than date_gt, and so on), and check that a multi-column sort string is sent unchanged. Add an example script. --- examples/rest/economy-funding_conditions.py | 13 +++ massive/rest/economy.py | 40 +++++++++ massive/rest/models/economy.py | 70 +++++++++++++++ ...=ZGF0ZS5ndGU9MjAyNS0wNi0wMSZsaW1pdD0x.json | 31 +++++++ ...onditions&date.gte=2025-06-01&limit=1.json | 32 +++++++ test_rest/test_economy.py | 89 +++++++++++++++++++ 6 files changed, 275 insertions(+) create mode 100644 examples/rest/economy-funding_conditions.py create mode 100644 test_rest/mocks/fed/v1/funding-conditions&cursor=ZGF0ZS5ndGU9MjAyNS0wNi0wMSZsaW1pdD0x.json create mode 100644 test_rest/mocks/fed/v1/funding-conditions&date.gte=2025-06-01&limit=1.json create mode 100644 test_rest/test_economy.py diff --git a/examples/rest/economy-funding_conditions.py b/examples/rest/economy-funding_conditions.py new file mode 100644 index 00000000..4dc948d2 --- /dev/null +++ b/examples/rest/economy-funding_conditions.py @@ -0,0 +1,13 @@ +from massive import RESTClient + +# docs +# https://massive.com/docs/rest/economy/funding-conditions + +# client = RESTClient("XXXXXX") # hardcoded api_key is used +client = RESTClient() # MASSIVE_API_KEY environment variable is used + +conditions = [] +for day in client.list_funding_conditions(date_gte="2025-06-01", sort="date.asc"): + conditions.append(day) + +print(conditions) diff --git a/massive/rest/economy.py b/massive/rest/economy.py index 4c4b251f..ddc4fb61 100644 --- a/massive/rest/economy.py +++ b/massive/rest/economy.py @@ -8,6 +8,7 @@ TreasuryYield, FedInflationExpectations, FedLaborMarket, + FedFundingConditions, EUMerchantAggregate, EUMerchantHierarchy, ) @@ -171,6 +172,45 @@ def list_labor_market_indicators( options=options, ) + def list_funding_conditions( + self, + date: Optional[Union[str, date]] = None, + date_gt: Optional[Union[str, date]] = None, + date_gte: Optional[Union[str, date]] = None, + date_lt: Optional[Union[str, date]] = None, + date_lte: Optional[Union[str, date]] = None, + limit: Optional[int] = None, + sort: Optional[str] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[Iterator[FedFundingConditions], HTTPResponse]: + """ + Daily U.S. money-market funding conditions from the Federal Reserve and the Federal Reserve Bank of New York, including the Federal Funds Effective Rate, SOFR, OBFR, tri-party general collateral repo rates, Treasury-collateralized overnight reverse repo and repo operation amounts, and commercial paper rates. + + :param date: Calendar date of the observation (YYYY-MM-DD). + :param date_gt: Filter greater than the value. + :param date_gte: Filter greater than or equal to the value. + :param date_lt: Filter less than the value. + :param date_lte: Filter less than or equal to the value. + :param limit: Limit the maximum number of results returned. Defaults to '100' if not specified. The maximum allowed limit is '50000'. + :param sort: A comma separated list of sort columns. For each column, append '.asc' or '.desc' to specify the sort direction, e.g. "date.desc". The sort column defaults to 'date' if not specified. The sort order defaults to 'asc' if not specified. The value is sent as given. + :param params: Additional query parameters. + :param raw: Return raw HTTPResponse object if True, else return Iterator[FedFundingConditions]. + :param options: RequestOptionBuilder for additional headers or params. + :return: An iterator of FedFundingConditions objects or HTTPResponse if raw=True. + """ + url = "/fed/v1/funding-conditions" + + return self._paginate( + path=url, + params=self._get_params(self.list_funding_conditions, locals()), + deserializer=FedFundingConditions.from_dict, + raw=raw, + result_key="results", + options=options, + ) + def list_eu_merchant_aggregates( self, transaction_date: Optional[Union[str, date]] = None, diff --git a/massive/rest/models/economy.py b/massive/rest/models/economy.py index 97386235..d0636f56 100644 --- a/massive/rest/models/economy.py +++ b/massive/rest/models/economy.py @@ -106,6 +106,76 @@ def from_dict(d): ) +@modelclass +class FedFundingConditions: + """ + Daily U.S. money-market funding conditions for a specific date. Rates are + percentages; volumes and operation amounts are in billions of U.S. dollars. + """ + + date: Optional[str] = None + effective_fed_funds_rate: Optional[float] = None + effective_fed_funds_volume: Optional[float] = None + fed_funds_target_lower: Optional[float] = None + fed_funds_target_upper: Optional[float] = None + fed_overnight_repo_treasury_amount: Optional[float] = None + fed_overnight_reverse_repo_treasury_amount: Optional[float] = None + financial_commercial_paper_90d_rate: Optional[float] = None + interest_on_reserve_balances: Optional[float] = None + nonfinancial_commercial_paper_90d_rate: Optional[float] = None + obfr_25th_percentile: Optional[float] = None + obfr_75th_percentile: Optional[float] = None + obfr_volume: Optional[float] = None + overnight_bank_funding_rate: Optional[float] = None + secured_overnight_financing_rate: Optional[float] = None + sofr_25th_percentile: Optional[float] = None + sofr_75th_percentile: Optional[float] = None + sofr_volume: Optional[float] = None + tgcr_25th_percentile: Optional[float] = None + tgcr_75th_percentile: Optional[float] = None + tri_party_general_collateral_rate: Optional[float] = None + tri_party_general_collateral_volume: Optional[float] = None + + @staticmethod + def from_dict(d): + return FedFundingConditions( + date=d.get("date"), + effective_fed_funds_rate=d.get("effective_fed_funds_rate"), + effective_fed_funds_volume=d.get("effective_fed_funds_volume"), + fed_funds_target_lower=d.get("fed_funds_target_lower"), + fed_funds_target_upper=d.get("fed_funds_target_upper"), + fed_overnight_repo_treasury_amount=d.get( + "fed_overnight_repo_treasury_amount" + ), + fed_overnight_reverse_repo_treasury_amount=d.get( + "fed_overnight_reverse_repo_treasury_amount" + ), + financial_commercial_paper_90d_rate=d.get( + "financial_commercial_paper_90d_rate" + ), + interest_on_reserve_balances=d.get("interest_on_reserve_balances"), + nonfinancial_commercial_paper_90d_rate=d.get( + "nonfinancial_commercial_paper_90d_rate" + ), + obfr_25th_percentile=d.get("obfr_25th_percentile"), + obfr_75th_percentile=d.get("obfr_75th_percentile"), + obfr_volume=d.get("obfr_volume"), + overnight_bank_funding_rate=d.get("overnight_bank_funding_rate"), + secured_overnight_financing_rate=d.get("secured_overnight_financing_rate"), + sofr_25th_percentile=d.get("sofr_25th_percentile"), + sofr_75th_percentile=d.get("sofr_75th_percentile"), + sofr_volume=d.get("sofr_volume"), + tgcr_25th_percentile=d.get("tgcr_25th_percentile"), + tgcr_75th_percentile=d.get("tgcr_75th_percentile"), + tri_party_general_collateral_rate=d.get( + "tri_party_general_collateral_rate" + ), + tri_party_general_collateral_volume=d.get( + "tri_party_general_collateral_volume" + ), + ) + + @modelclass class EUMerchantAggregate: """ diff --git a/test_rest/mocks/fed/v1/funding-conditions&cursor=ZGF0ZS5ndGU9MjAyNS0wNi0wMSZsaW1pdD0x.json b/test_rest/mocks/fed/v1/funding-conditions&cursor=ZGF0ZS5ndGU9MjAyNS0wNi0wMSZsaW1pdD0x.json new file mode 100644 index 00000000..1f20e1b3 --- /dev/null +++ b/test_rest/mocks/fed/v1/funding-conditions&cursor=ZGF0ZS5ndGU9MjAyNS0wNi0wMSZsaW1pdD0x.json @@ -0,0 +1,31 @@ +{ + "status": "OK", + "request_id": "b2170df985474b6d21a6eeccfb6bee67", + "count": 1, + "results": [ + { + "date": "2025-06-02", + "effective_fed_funds_rate": 4.33, + "effective_fed_funds_volume": 98, + "fed_funds_target_lower": 4.25, + "fed_funds_target_upper": 4.5, + "fed_overnight_repo_treasury_amount": 0, + "fed_overnight_reverse_repo_treasury_amount": 214.5, + "financial_commercial_paper_90d_rate": 4.35, + "interest_on_reserve_balances": 4.4, + "nonfinancial_commercial_paper_90d_rate": 4.34, + "obfr_25th_percentile": 4.3, + "obfr_75th_percentile": 4.33, + "obfr_volume": 305, + "overnight_bank_funding_rate": 4.32, + "secured_overnight_financing_rate": 4.31, + "sofr_25th_percentile": 4.28, + "sofr_75th_percentile": 4.32, + "sofr_volume": 2214, + "tgcr_25th_percentile": 4.27, + "tgcr_75th_percentile": 4.31, + "tri_party_general_collateral_rate": 4.3, + "tri_party_general_collateral_volume": 1876 + } + ] +} diff --git a/test_rest/mocks/fed/v1/funding-conditions&date.gte=2025-06-01&limit=1.json b/test_rest/mocks/fed/v1/funding-conditions&date.gte=2025-06-01&limit=1.json new file mode 100644 index 00000000..f294c384 --- /dev/null +++ b/test_rest/mocks/fed/v1/funding-conditions&date.gte=2025-06-01&limit=1.json @@ -0,0 +1,32 @@ +{ + "status": "OK", + "request_id": "6a7e466379af0a71039d60cc78e72282", + "count": 1, + "next_url": "https://api.massive.com/fed/v1/funding-conditions?cursor=ZGF0ZS5ndGU9MjAyNS0wNi0wMSZsaW1pdD0x", + "results": [ + { + "date": "2025-06-01", + "effective_fed_funds_rate": 4.33, + "effective_fed_funds_volume": null, + "fed_funds_target_lower": 4.25, + "fed_funds_target_upper": 4.5, + "fed_overnight_repo_treasury_amount": null, + "fed_overnight_reverse_repo_treasury_amount": null, + "financial_commercial_paper_90d_rate": null, + "interest_on_reserve_balances": 4.4, + "nonfinancial_commercial_paper_90d_rate": null, + "obfr_25th_percentile": null, + "obfr_75th_percentile": null, + "obfr_volume": null, + "overnight_bank_funding_rate": null, + "secured_overnight_financing_rate": null, + "sofr_25th_percentile": null, + "sofr_75th_percentile": null, + "sofr_volume": null, + "tgcr_25th_percentile": null, + "tgcr_75th_percentile": null, + "tri_party_general_collateral_rate": null, + "tri_party_general_collateral_volume": null + } + ] +} diff --git a/test_rest/test_economy.py b/test_rest/test_economy.py new file mode 100644 index 00000000..223475f1 --- /dev/null +++ b/test_rest/test_economy.py @@ -0,0 +1,89 @@ +import pook # type: ignore + +from base import BaseTest +from massive.rest.models.economy import FedFundingConditions + + +class EconomyTest(BaseTest): + def test_list_funding_conditions(self): + conditions = [ + c for c in self.c.list_funding_conditions(date_gte="2025-06-01", limit=1) + ] + expected = [ + FedFundingConditions( + date="2025-06-01", + effective_fed_funds_rate=4.33, + fed_funds_target_lower=4.25, + fed_funds_target_upper=4.5, + interest_on_reserve_balances=4.4, + ), + FedFundingConditions( + date="2025-06-02", + effective_fed_funds_rate=4.33, + effective_fed_funds_volume=98, + fed_funds_target_lower=4.25, + fed_funds_target_upper=4.5, + fed_overnight_repo_treasury_amount=0, + fed_overnight_reverse_repo_treasury_amount=214.5, + financial_commercial_paper_90d_rate=4.35, + interest_on_reserve_balances=4.4, + nonfinancial_commercial_paper_90d_rate=4.34, + obfr_25th_percentile=4.3, + obfr_75th_percentile=4.33, + obfr_volume=305, + overnight_bank_funding_rate=4.32, + secured_overnight_financing_rate=4.31, + sofr_25th_percentile=4.28, + sofr_75th_percentile=4.32, + sofr_volume=2214, + tgcr_25th_percentile=4.27, + tgcr_75th_percentile=4.31, + tri_party_general_collateral_rate=4.3, + tri_party_general_collateral_volume=1876, + ), + ] + self.assertEqual(conditions, expected) + + def test_list_funding_conditions_query_params(self): + # each documented parameter is sent under its API name (date.gt, not date_gt) + params = { + "date": "2025-06-02", + "date.gt": "2025-06-01", + "date.gte": "2025-06-01", + "date.lt": "2025-06-03", + "date.lte": "2025-06-03", + "limit": "10", + "sort": "date.desc", + } + mock = pook.get( + self.c.BASE + "/fed/v1/funding-conditions", + params=params, + reply=200, + response_json={"results": []}, + ) + conditions = self.c.list_funding_conditions( + date="2025-06-02", + date_gt="2025-06-01", + date_gte="2025-06-01", + date_lt="2025-06-03", + date_lte="2025-06-03", + limit=10, + sort="date.desc", + ) + self.assertEqual(list(conditions), []) + self.assertEqual(mock.calls, 1) + self.assertEqual(mock.matches[0].query, {k: [v] for k, v in params.items()}) + + def test_list_funding_conditions_sort_is_sent_as_given(self): + # sort takes "." values, comma separated, not a + # bare direction, so the client must send the string unchanged. + sort = "date.desc,effective_fed_funds_rate.asc" + mock = pook.get( + self.c.BASE + "/fed/v1/funding-conditions", + params={"sort": sort}, + reply=200, + response_json={"results": []}, + ) + self.assertEqual(list(self.c.list_funding_conditions(sort=sort)), []) + self.assertEqual(mock.calls, 1) + self.assertEqual(mock.matches[0].query, {"sort": [sort]}) From 6ead873b22b5f980cdd8b520b91d95fb13c6f414 Mon Sep 17 00:00:00 2001 From: Chip Kent <5250374+chipkent@users.noreply.github.com> Date: Fri, 23 Oct 2026 17:09:00 -0600 Subject: [PATCH 2/2] feat: add get_daily_open_close_crypto for /v1/open-close/crypto/{from}/{to}/{date} The crypto daily open/close endpoint is documented in the OpenAPI spec and on massive.com/docs but had no client method. The docs and the crypto example pointed at get_daily_open_close_agg, which calls /v1/open-close/{ticker}/{date}, a different endpoint whose response (afterHours, preMarket, high, low, volume) does not match the crypto one. Add AggsClient.get_daily_open_close_crypto(from_, to, date, adjusted) and two models matching the documented response: - DailyOpenCloseCrypto: symbol, is_utc (isUTC), day, open, close, open_trades (openTrades), closing_trades (closingTrades). - DailyOpenCloseCryptoTrade: conditions (c), id (i), price (p), size (s), timestamp (t), exchange (x). Test against a fixture built from the documented example response. Give the method its own section in the Aggs docs, move the crypto docs link there, and point the crypto example at the new method. --- docs/source/Aggs.rst | 9 ++- examples/rest/crypto-daily_open_close.py | 7 ++- massive/rest/aggs.py | 41 ++++++++++++- massive/rest/models/aggs.py | 60 ++++++++++++++++++- .../BTC/USD/2020-10-09&adjusted=true.json | 51 ++++++++++++++++ test_rest/test_aggs.py | 52 ++++++++++++++++ 6 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 test_rest/mocks/v1/open-close/crypto/BTC/USD/2020-10-09&adjusted=true.json diff --git a/docs/source/Aggs.rst b/docs/source/Aggs.rst index b8e3fcc8..45fccd7c 100644 --- a/docs/source/Aggs.rst +++ b/docs/source/Aggs.rst @@ -41,10 +41,17 @@ Get daily open close agg - `Stocks daily open/close agg`_ - `Options daily open/close agg`_ -- `Crypto daily open/close agg`_ .. automethod:: massive.RESTClient.get_daily_open_close_agg +============================ +Get daily open close crypto +============================ + +- `Crypto daily open/close agg`_ + +.. automethod:: massive.RESTClient.get_daily_open_close_crypto + ============================ Get previous close agg ============================ diff --git a/examples/rest/crypto-daily_open_close.py b/examples/rest/crypto-daily_open_close.py index 5c29db0c..41971796 100644 --- a/examples/rest/crypto-daily_open_close.py +++ b/examples/rest/crypto-daily_open_close.py @@ -2,14 +2,15 @@ # docs # https://massive.com/docs/crypto/get_v1_open-close_crypto__from___to___date -# https://massive-api-client.readthedocs.io/en/latest/Aggs.html#get-daily-open-close-agg +# https://massive-api-client.readthedocs.io/en/latest/Aggs.html#get-daily-open-close-crypto # client = RESTClient("XXXXXX") # hardcoded api_key is used client = RESTClient() # MASSIVE_API_KEY environment variable is used # make request -request = client.get_daily_open_close_agg( - "X:BTCUSD", +request = client.get_daily_open_close_crypto( + "BTC", + "USD", "2023-01-09", ) diff --git a/massive/rest/aggs.py b/massive/rest/aggs.py index cb874fac..adfab927 100644 --- a/massive/rest/aggs.py +++ b/massive/rest/aggs.py @@ -1,6 +1,13 @@ from .base import BaseClient from typing import Optional, Any, Dict, List, Union, Iterator -from .models import Agg, GroupedDailyAgg, DailyOpenCloseAgg, PreviousCloseAgg, Sort +from .models import ( + Agg, + GroupedDailyAgg, + DailyOpenCloseAgg, + DailyOpenCloseCrypto, + PreviousCloseAgg, + Sort, +) from urllib3 import HTTPResponse from datetime import datetime, date @@ -161,6 +168,38 @@ def get_daily_open_close_agg( options=options, ) + def get_daily_open_close_crypto( + self, + # "from" is a keyword in python https://www.w3schools.com/python/python_ref_keywords.asp + from_: str, + to: str, + date: Union[str, date], + adjusted: Optional[bool] = None, + params: Optional[Dict[str, Any]] = None, + raw: bool = False, + options: Optional[RequestOptionBuilder] = None, + ) -> Union[DailyOpenCloseCrypto, HTTPResponse]: + """ + Get the open and close prices of a cryptocurrency pair on a certain day, along with the opening and closing trades. + + :param from_: The "from" symbol of the pair (e.g. "BTC"). + :param to: The "to" symbol of the pair (e.g. "USD"). + :param date: The date of the requested open/close in the format YYYY-MM-DD. + :param adjusted: Whether or not the results are adjusted for splits. By default, results are adjusted. Set this to false to get results that are NOT adjusted for splits. + :param params: Any additional query params + :param raw: Return raw object instead of results object + :return: Daily open close for the crypto pair + """ + url = f"/v1/open-close/crypto/{from_}/{to}/{date}" + + return self._get( + path=url, + params=self._get_params(self.get_daily_open_close_crypto, locals()), + deserializer=DailyOpenCloseCrypto.from_dict, + raw=raw, + options=options, + ) + def get_previous_close_agg( self, ticker: str, diff --git a/massive/rest/models/aggs.py b/massive/rest/models/aggs.py index 49bba680..7315237e 100644 --- a/massive/rest/models/aggs.py +++ b/massive/rest/models/aggs.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional from ...modelclass import modelclass @@ -95,6 +95,64 @@ def from_dict(d): ) +@modelclass +class DailyOpenCloseCryptoTrade: + "Contains data for one of the opening or closing trades of a crypto pair on a specified date." + + conditions: Optional[List[int]] = None + id: Optional[str] = None + price: Optional[float] = None + size: Optional[float] = None + timestamp: Optional[int] = None + exchange: Optional[int] = None + + @staticmethod + def from_dict(d): + return DailyOpenCloseCryptoTrade( + conditions=d.get("c", None), + id=d.get("i", None), + price=d.get("p", None), + size=d.get("s", None), + timestamp=d.get("t", None), + exchange=d.get("x", None), + ) + + +@modelclass +class DailyOpenCloseCrypto: + "Contains data for open and close prices, and the opening and closing trades, of a crypto pair on a specified date." + + symbol: Optional[str] = None + is_utc: Optional[bool] = None + day: Optional[str] = None + open: Optional[float] = None + close: Optional[float] = None + open_trades: Optional[List[DailyOpenCloseCryptoTrade]] = None + closing_trades: Optional[List[DailyOpenCloseCryptoTrade]] = None + + @staticmethod + def from_dict(d): + return DailyOpenCloseCrypto( + symbol=d.get("symbol", None), + is_utc=d.get("isUTC", None), + day=d.get("day", None), + open=d.get("open", None), + close=d.get("close", None), + open_trades=( + None + if "openTrades" not in d + else [DailyOpenCloseCryptoTrade.from_dict(t) for t in d["openTrades"]] + ), + closing_trades=( + None + if "closingTrades" not in d + else [ + DailyOpenCloseCryptoTrade.from_dict(t) for t in d["closingTrades"] + ] + ), + ) + + @modelclass class PreviousCloseAgg: "Contains data for the previous day's open, high, low, and close (OHLC) of the specified stock ticker." diff --git a/test_rest/mocks/v1/open-close/crypto/BTC/USD/2020-10-09&adjusted=true.json b/test_rest/mocks/v1/open-close/crypto/BTC/USD/2020-10-09&adjusted=true.json new file mode 100644 index 00000000..b3338177 --- /dev/null +++ b/test_rest/mocks/v1/open-close/crypto/BTC/USD/2020-10-09&adjusted=true.json @@ -0,0 +1,51 @@ +{ + "symbol": "BTC-USD", + "isUTC": true, + "day": "2020-10-09T00:00:00.000Z", + "open": 10932.44, + "close": 11050.64, + "openTrades": [ + { + "s": 0.002, + "p": 10932.44, + "x": 1, + "t": 1602201600056, + "c": [ + 2 + ], + "i": "511235746" + }, + { + "s": 0.02, + "p": 10923.76, + "x": 4, + "t": 1602201600141, + "c": [ + 2 + ], + "i": "511235751" + } + ], + "closingTrades": [ + { + "s": 0.006128, + "p": 11050.64, + "x": 4, + "t": 1602287999795, + "c": [ + 2 + ], + "i": "973323250" + }, + { + "s": 0.014, + "p": 11049.4, + "x": 17, + "t": 1602287999659, + "c": [ + 1 + ], + "i": "105717893" + } + ] +} diff --git a/test_rest/test_aggs.py b/test_rest/test_aggs.py index 850a00da..cff53ee1 100644 --- a/test_rest/test_aggs.py +++ b/test_rest/test_aggs.py @@ -3,6 +3,8 @@ Agg, GroupedDailyAgg, DailyOpenCloseAgg, + DailyOpenCloseCrypto, + DailyOpenCloseCryptoTrade, PreviousCloseAgg, ) @@ -96,6 +98,56 @@ def test_get_daily_open_close_agg(self): self.assertEqual(aggs, expected) + def test_get_daily_open_close_crypto(self): + aggs = self.c.get_daily_open_close_crypto( + "BTC", "USD", "2020-10-09", adjusted=True + ) + expected = DailyOpenCloseCrypto( + symbol="BTC-USD", + is_utc=True, + day="2020-10-09T00:00:00.000Z", + open=10932.44, + close=11050.64, + open_trades=[ + DailyOpenCloseCryptoTrade( + conditions=[2], + id="511235746", + price=10932.44, + size=0.002, + timestamp=1602201600056, + exchange=1, + ), + DailyOpenCloseCryptoTrade( + conditions=[2], + id="511235751", + price=10923.76, + size=0.02, + timestamp=1602201600141, + exchange=4, + ), + ], + closing_trades=[ + DailyOpenCloseCryptoTrade( + conditions=[2], + id="973323250", + price=11050.64, + size=0.006128, + timestamp=1602287999795, + exchange=4, + ), + DailyOpenCloseCryptoTrade( + conditions=[1], + id="105717893", + price=11049.4, + size=0.014, + timestamp=1602287999659, + exchange=17, + ), + ], + ) + + self.assertEqual(aggs, expected) + def test_get_previous_close_agg(self): aggs = self.c.get_previous_close_agg("AAPL") expected = [