diff --git a/CHANGELOG.md b/CHANGELOG.md index 176ec45..272f36b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [1.4.0] - 2024-MAY-21 + +### Added +- Support for market order buys in base currency with `base_size` in the `market_order_buy` and `preview_market_order_buy` methods +- Support for the following Futures Intraday Leverage API endpoints: + - GetCurrentMarginWindow + - GetIntradayMarginSetting + - SetIntradayMarginSetting +- Support for the following Perpetual Futures Trading API endpoints: + - Get Perpetuals Portfolio Asset Balances + - Opt-In Multi Asset Collateral + ## [1.3.0] - 2024-MAY-6 ### Added diff --git a/README.md b/README.md index 9c36bc1..f0a107f 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/license/apache-2-0/) [![Code Style](https://img.shields.io/badge/code_style-black-black)](https://black.readthedocs.io/en/stable/) -Welcome to the official Coinbase Advanced API Python SDK. This python project was created to allow coders to easily plug into the [Coinbase Advanced API](https://docs.cloud.coinbase.com/advanced-trade-api/docs/welcome). -This SDK also supports easy connection to the [Coinbase Advanced Trade WebSocket API](https://docs.cloud.coinbase.com/advanced-trade-api/docs/ws-overview). +Welcome to the official Coinbase Advanced API Python SDK. This python project was created to allow coders to easily plug into the [Coinbase Advanced API](https://docs.cdp.coinbase.com/advanced-trade/docs/welcome). +This SDK also supports easy connection to the [Coinbase Advanced Trade WebSocket API](https://docs.cdp.coinbase.com/advanced-trade/docs/ws-overview). For thorough documentation of all available functions, refer to the following link: https://coinbase.github.io/coinbase-advanced-py @@ -18,7 +18,7 @@ pip3 install coinbase-advanced-py ___ ## Coinbase Developer Platform (CDP) API Keys -This SDK uses Cloud Developer Platform (CDP) API keys. To use this SDK, you will need to create a CDP API key and secret by following the instructions [here](https://docs.cloud.coinbase.com/advanced-trade-api/docs/auth#cloud-api-keys). +This SDK uses Cloud Developer Platform (CDP) API keys. To use this SDK, you will need to create a CDP API key and secret by following the instructions [here](https://docs.cdp.coinbase.com/advanced-trade/docs/auth). Make sure to save your API key and secret in a safe place. You will not be able to retrieve your secret again. WARNING: We do not recommend that you save your API secrets directly in your code outside of testing purposes. Best practice is to use a secrets manager and access your secrets that way. You should be careful about exposing your secrets publicly if posting code that leverages this library. @@ -74,7 +74,7 @@ print(dumps(order, indent=2)) ``` This code calls the `get_accounts` and `market_order_buy` endpoints. -Refer to the [Advanced API Reference](https://docs.cloud.coinbase.com/advanced-trade-api/reference) for detailed information on each exposed endpoint. +Refer to the [Advanced API Reference](https://docs.cdp.coinbase.com/advanced-trade/reference) for detailed information on each exposed endpoint. Look in the `coinbase.rest` module to see the API hooks that are exposed. ### Passing in additional parameters @@ -94,7 +94,7 @@ market_trades = client.get("/api/v3/brokerage/products/BTC-USD/ticker", params={ portfolio = client.post("/api/v3/brokerage/portfolios", data={"name": "TestPortfolio"}) ``` -Here we are calling the [GetMarketTrades](https://docs.cloud.coinbase.com/advanced-trade-api/reference/retailbrokerageapi_getmarkettrades) and [CreatePortfolio](https://docs.cloud.coinbase.com/advanced-trade-api/reference/retailbrokerageapi_createportfolio) endpoints through the generic REST functions. +Here we are calling the [GetMarketTrades](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getmarkettrades) and [CreatePortfolio](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_createportfolio) endpoints through the generic REST functions. Once again, the built-in way to query these through the SDK would be: ```python market_trades = client.get_market_trades(product_id="BTC-USD", limit=5) @@ -104,8 +104,8 @@ portfolio = client.create_portfolio(name="TestPortfolio") ___ ## WebSocket API Client -We offer a WebSocket API client that allows you to connect to the [Coinbase Advanced Trade WebSocket API](https://docs.cloud.coinbase.com/advanced-trade-api/docs/ws-overview). -Refer to the [Advanced Trade WebSocket Channels](https://docs.cloud.coinbase.com/advanced-trade-api/docs/ws-channels) page for detailed information on each offered channel. +We offer a WebSocket API client that allows you to connect to the [Coinbase Advanced Trade WebSocket API](https://docs.cdp.coinbase.com/advanced-trade/docs/ws-overview). +Refer to the [Advanced Trade WebSocket Channels](https://docs.cdp.coinbase.com/advanced-trade/docs/ws-channels) page for detailed information on each offered channel. In your code, import the WSClient class and instantiate it. The WSClient requires an API key and secret to be passed in as arguments. You can also use a key file or environment variables as described in the RESTClient instructions above. @@ -241,7 +241,7 @@ ___ ## Authentication Authentication of CDP API Keys is handled automatically by the SDK when making a REST request or sending a WebSocket message. -However, if you wish to handle this yourself, you must create a JWT token and attach it to your request as detailed in the API docs [here](https://docs.cloud.coinbase.com/advanced-trade-api/docs/rest-api-auth#making-requests). Use the built in `jwt_generator` to create your JWT token. For example: +However, if you wish to handle this yourself, you must create a JWT token and attach it to your request as detailed in the API docs [here](https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-auth#making-requests). Use the built in `jwt_generator` to create your JWT token. For example: ```python from coinbase import jwt_generator @@ -267,7 +267,7 @@ api_secret = "-----BEGIN EC PRIVATE KEY-----\nYOUR PRIVATE KEY\n-----END EC PRIV jwt = jwt_generator.build_ws_jwt(api_key, api_secret) ``` -Use this JWT to connect to the Websocket API by setting it in the "jwt" field of your subscription requests. See the docs [here](https://docs.cloud.coinbase.com/advanced-trade-api/docs/ws-overview#sending-messages-using-cloud-api-keys) for more details. +Use this JWT to connect to the Websocket API by setting it in the "jwt" field of your subscription requests. See the docs [here](https://docs.cdp.coinbase.com/advanced-trade/docs/ws-overview#sending-messages-with-cdp-keys) for more details. ___ ## Accessing public endpoints without authentication @@ -283,9 +283,9 @@ To do so, simply initialize the clients without providing any API keys as argume ### REST Client -In the REST client, here is an example calling [Get Public Products](https://docs.cloud.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpublicproducts). +In the REST client, here is an example calling [Get Public Products](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getpublicproducts). It does _not_ require authentication and is the public counterpart to -[Get Products](https://docs.cloud.coinbase.com/advanced-trade/reference/retailbrokerageapi_getproducts), which _does_ require authentication. +[Get Products](https://docs.cdp.coinbase.com/advanced-trade/reference/retailbrokerageapi_getproducts), which _does_ require authentication. Both endpoints return the same data. @@ -298,13 +298,13 @@ client = RESTClient() public_products = client.get_public_products() print(public_products) ``` -_Full list of all public REST endpoints [here](https://docs.cloud.coinbase.com/advanced-trade/docs/rest-api-overview#public-endpoints)_ +_Full list of all public REST endpoints [here](https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-overview#public-endpoints)_ -_Rate limit details for REST endpoints [here](https://docs.cloud.coinbase.com/advanced-trade/docs/rest-api-rate-limits)_ +_Rate limit details for REST endpoints [here](https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-rate-limits)_ ### Websocket Client -In the Websocket client, here is an example subscribing to the [ticker](https://docs.cloud.coinbase.com/advanced-trade/docs/ws-channels#ticker-channel) channel. +In the Websocket client, here is an example subscribing to the [ticker](https://docs.cdp.coinbase.com/advanced-trade/docs/ws-channels#ticker-channel) channel. Unlike the REST client, Websocket channels handle both authenticated and unauthenticated requests. At the moment, most channels in the Websocket client are public and can be used without keys. @@ -326,9 +326,9 @@ client.ticker_unsubscribe(product_ids=["BTC-USD"]) client.close() ``` -_Full list of all public Websocket channels [here](https://docs.cloud.coinbase.com/advanced-trade/docs/ws-channels)_ +_Full list of all public Websocket channels [here](https://docs.cdp.coinbase.com/advanced-trade/docs/ws-channels)_ -_Rate limit details for Websocket channels [here](https://docs.cloud.coinbase.com/advanced-trade/docs/ws-rate-limits)_ +_Rate limit details for Websocket channels [here](https://docs.cdp.coinbase.com/advanced-trade/docs/ws-rate-limits)_ ___ @@ -340,4 +340,4 @@ ___ If you've found a bug within this project, open an issue on this repo and add the "bug" label to it. If you would like to request a new feature, open an issue on this repo and add the "enhancement" label to it. -Direct concerns or questions on the API to the [Advanced API Developer Forum](https://forums.coinbasecloud.dev/c/advanced-trade-api/20). +Direct concerns or questions on the API to the [Advanced API Discord](https://discord.com/channels/1220414409550336183/1220464268743278613) (use this [invite link](https://discord.com/invite/cdp) if it's your first time accessing the Discord). diff --git a/coinbase/__version__.py b/coinbase/__version__.py index 67bc602..3e8d9f9 100644 --- a/coinbase/__version__.py +++ b/coinbase/__version__.py @@ -1 +1 @@ -__version__ = "1.3.0" +__version__ = "1.4.0" diff --git a/coinbase/rest/__init__.py b/coinbase/rest/__init__.py index 4d05cfe..44bf028 100755 --- a/coinbase/rest/__init__.py +++ b/coinbase/rest/__init__.py @@ -8,11 +8,14 @@ class RESTClient(RESTBase): from .futures import ( cancel_pending_futures_sweep, close_position, + get_current_margin_window, get_futures_balance_summary, get_futures_position, + get_intraday_margin_setting, list_futures_positions, list_futures_sweeps, schedule_futures_sweep, + set_intraday_margin_setting, ) from .market_data import get_candles, get_market_trades from .orders import ( @@ -82,9 +85,11 @@ class RESTClient(RESTBase): from .payments import get_payment_method, list_payment_methods from .perpetuals import ( allocate_portfolio, + get_perps_portfolio_balances, get_perps_portfolio_summary, get_perps_position, list_perps_positions, + opt_in_or_out_multi_asset_collateral, ) from .portfolios import ( create_portfolio, diff --git a/coinbase/rest/accounts.py b/coinbase/rest/accounts.py index b841d0b..5b920f2 100644 --- a/coinbase/rest/accounts.py +++ b/coinbase/rest/accounts.py @@ -23,7 +23,7 @@ def get_accounts( __________ - **Read more on the official documentation:** `List Accounts `_ + **Read more on the official documentation:** `List Accounts `_ """ endpoint = f"{API_PREFIX}/accounts" @@ -51,7 +51,7 @@ def get_account(self, account_uuid: str, **kwargs) -> Dict[str, Any]: __________ - **Read more on the official documentation:** `Get Account `_ + **Read more on the official documentation:** `Get Account `_ """ endpoint = f"{API_PREFIX}/accounts/{account_uuid}" diff --git a/coinbase/rest/convert.py b/coinbase/rest/convert.py index a3b3ccd..c7d5fb7 100644 --- a/coinbase/rest/convert.py +++ b/coinbase/rest/convert.py @@ -26,7 +26,7 @@ def create_convert_quote( __________ - **Read more on the official documentation:** `Create Convert Quote `_ + **Read more on the official documentation:** `Create Convert Quote `_ """ endpoint = f"{API_PREFIX}/convert/quote" @@ -69,7 +69,7 @@ def get_convert_trade( __________ - **Read more on the official documentation:** `Get Convert Trade `_ + **Read more on the official documentation:** `Get Convert Trade `_ """ endpoint = f"{API_PREFIX}/convert/trade/{trade_id}" @@ -98,7 +98,7 @@ def commit_convert_trade( __________ - **Read more on the official documentation:** `Commit Convert Trade `_ + **Read more on the official documentation:** `Commit Convert Trade `_ """ endpoint = f"{API_PREFIX}/convert/trade/{trade_id}" diff --git a/coinbase/rest/fees.py b/coinbase/rest/fees.py index 5b65e00..a2ba3e5 100644 --- a/coinbase/rest/fees.py +++ b/coinbase/rest/fees.py @@ -23,7 +23,7 @@ def get_transaction_summary( __________ - **Read more on the official documentation:** `Create Convert Quote `_ + **Read more on the official documentation:** `Create Convert Quote `_ """ endpoint = f"{API_PREFIX}/transaction_summary" diff --git a/coinbase/rest/futures.py b/coinbase/rest/futures.py index 7f0ad7d..456c7c1 100644 --- a/coinbase/rest/futures.py +++ b/coinbase/rest/futures.py @@ -21,7 +21,7 @@ def close_position( __________ **Read more on the official documentation:** `Close Position - `_ + `_ """ endpoint = f"{API_PREFIX}/orders/close_position" data = {"client_order_id": client_order_id, "product_id": product_id, "size": size} @@ -45,7 +45,7 @@ def get_futures_balance_summary(self, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Get Futures Balance Summary - `_ + `_ """ endpoint = f"{API_PREFIX}/cfm/balance_summary" @@ -68,7 +68,7 @@ def list_futures_positions(self, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `List Futures Positions - `_ + `_ """ endpoint = f"{API_PREFIX}/cfm/positions" @@ -91,7 +91,7 @@ def get_futures_position(self, product_id: str, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Get Futures Position - `_ + `_ """ endpoint = f"{API_PREFIX}/cfm/positions/{product_id}" @@ -114,7 +114,7 @@ def schedule_futures_sweep(self, usd_amount: str, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Schedule Futures Sweep - `_ + `_ """ endpoint = f"{API_PREFIX}/cfm/sweeps/schedule" @@ -139,7 +139,7 @@ def list_futures_sweeps(self, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `List Futures Sweeps - `_ + `_ """ endpoint = f"{API_PREFIX}/cfm/sweeps" @@ -162,8 +162,85 @@ def cancel_pending_futures_sweep(self, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Cancel Pending Futures Sweep - `_ + `_ """ endpoint = f"{API_PREFIX}/cfm/sweeps" return self.delete(endpoint, **kwargs) + + +def get_intraday_margin_setting(self, **kwargs) -> Dict[str, Any]: + """ + **Get Intraday Margin Setting** + _______________________________ + + [GET] https://api.coinbase.com/api/v3/brokerage/cfm/intraday/margin_setting + + __________ + + **Description:** + + Get the status of whether your account is opted in to receive increased leverage on futures trades on weekdays from 8am-4pm ET. + + __________ + + **Read more on the official documentation:** `Get Intraday Margin Setting + `_ + """ + endpoint = f"{API_PREFIX}/cfm/intraday/margin_setting" + + return self.get(endpoint, **kwargs) + + +def get_current_margin_window( + self, margin_profile_type: str, **kwargs +) -> Dict[str, Any]: + """ + **Get Current Margin Window** + ________________________________ + + [GET] https://api.coinbase.com/api/v3/brokerage/cfm/intraday/current_margin_window + + __________ + + **Description:** + + Get the current margin window to determine whether intraday or overnight margin rates are in effect. + + __________ + + **Read more on the official documentation:** `Get Current Margin Window + `_ + """ + + endpoint = f"{API_PREFIX}/cfm/intraday/current_margin_window" + + params = {"margin_profile_type": margin_profile_type} + + return self.get(endpoint, params=params, **kwargs) + + +def set_intraday_margin_setting(self, setting: str, **kwargs) -> Dict[str, Any]: + """ + **Set Intraday Margin Setting** + ________________________________ + + [POST] https://api.coinbase.com/api/v3/brokerage/cfm/intraday/margin_setting + + __________ + + **Description:** + + Opt in to receive increased leverage on futures trades on weekdays from 8am-4pm ET. + + __________ + + **Read more on the official documentation:** `Set Intraday Margin Setting + `_ + """ + + endpoint = f"{API_PREFIX}/cfm/intraday/margin_setting" + + data = {"setting": setting} + + return self.post(endpoint, data=data, **kwargs) diff --git a/coinbase/rest/market_data.py b/coinbase/rest/market_data.py index eef5b9a..e415e68 100644 --- a/coinbase/rest/market_data.py +++ b/coinbase/rest/market_data.py @@ -21,7 +21,7 @@ def get_candles( __________ **Read more on the official documentation:** `Get Product Candles - `_ + `_ """ endpoint = f"{API_PREFIX}/products/{product_id}/candles" @@ -57,7 +57,7 @@ def get_market_trades( __________ **Read more on the official documentation:** `Get Market Trades - `_ + `_ """ endpoint = f"{API_PREFIX}/products/{product_id}/ticker" diff --git a/coinbase/rest/orders.py b/coinbase/rest/orders.py index eae01b4..0a4c141 100644 --- a/coinbase/rest/orders.py +++ b/coinbase/rest/orders.py @@ -30,7 +30,7 @@ def create_order( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ endpoint = f"{API_PREFIX}/orders" @@ -78,7 +78,7 @@ def market_order( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ market_market_ioc = {"quote_size": quote_size, "base_size": base_size} @@ -106,7 +106,8 @@ def market_order_buy( self, client_order_id: str, product_id: str, - quote_size: str, + quote_size: Optional[str] = None, + base_size: Optional[str] = None, self_trade_prevention_id: Optional[str] = None, leverage: Optional[str] = None, margin_type: Optional[str] = None, @@ -128,7 +129,7 @@ def market_order_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return market_order( self, @@ -136,6 +137,7 @@ def market_order_buy( product_id, "BUY", quote_size=quote_size, + base_size=base_size, self_trade_prevention_id=self_trade_prevention_id, leverage=leverage, margin_type=margin_type, @@ -170,7 +172,7 @@ def market_order_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return market_order( self, @@ -216,7 +218,7 @@ def limit_order_ioc( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ sor_limit_ioc = {"base_size": base_size, "limit_price": limit_price} @@ -265,7 +267,7 @@ def limit_order_ioc_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return limit_order_ioc( @@ -311,7 +313,7 @@ def limit_order_ioc_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return limit_order_ioc( @@ -360,7 +362,7 @@ def limit_order_gtc( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ order_configuration = { "limit_limit_gtc": { @@ -413,7 +415,7 @@ def limit_order_gtc_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return limit_order_gtc( self, @@ -460,7 +462,7 @@ def limit_order_gtc_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return limit_order_gtc( self, @@ -510,7 +512,7 @@ def limit_order_gtd( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ order_configuration = { "limit_limit_gtd": { @@ -565,7 +567,7 @@ def limit_order_gtd_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return limit_order_gtd( self, @@ -614,7 +616,7 @@ def limit_order_gtd_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return limit_order_gtd( self, @@ -663,7 +665,7 @@ def limit_order_fok( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ limit_limit_fok = {"base_size": base_size, "limit_price": limit_price} @@ -712,7 +714,7 @@ def limit_order_fok_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return limit_order_fok( @@ -758,7 +760,7 @@ def limit_order_fok_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return limit_order_fok( @@ -808,7 +810,7 @@ def stop_limit_order_gtc( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ order_configuration = { "stop_limit_stop_limit_gtc": { @@ -863,7 +865,7 @@ def stop_limit_order_gtc_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return stop_limit_order_gtc( self, @@ -912,7 +914,7 @@ def stop_limit_order_gtc_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return stop_limit_order_gtc( self, @@ -964,7 +966,7 @@ def stop_limit_order_gtd( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ order_configuration = { "stop_limit_stop_limit_gtd": { @@ -1021,7 +1023,7 @@ def stop_limit_order_gtd_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return stop_limit_order_gtd( self, @@ -1072,7 +1074,7 @@ def stop_limit_order_gtd_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return stop_limit_order_gtd( self, @@ -1123,7 +1125,7 @@ def trigger_bracket_order_gtc( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ order_configuration = { "trigger_bracket_gtc": { @@ -1176,7 +1178,7 @@ def trigger_bracket_order_gtc_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return trigger_bracket_order_gtc( self, @@ -1223,7 +1225,7 @@ def trigger_bracket_order_gtc_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return trigger_bracket_order_gtc( self, @@ -1273,7 +1275,7 @@ def trigger_bracket_order_gtd( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ order_configuration = { "trigger_bracket_gtd": { @@ -1328,7 +1330,7 @@ def trigger_bracket_order_gtd_buy( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return trigger_bracket_order_gtd( self, @@ -1377,7 +1379,7 @@ def trigger_bracket_order_gtd_sell( __________ **Read more on the official documentation:** `Create Order - `_ + `_ """ return trigger_bracket_order_gtd( self, @@ -1412,7 +1414,7 @@ def get_order(self, order_id: str, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Get Order - `_ + `_ """ endpoint = f"{API_PREFIX}/orders/historical/{order_id}" @@ -1451,7 +1453,7 @@ def list_orders( __________ **Read more on the official documentation:** `List Orders - `_ + `_ """ endpoint = f"{API_PREFIX}/orders/historical/batch" params = { @@ -1498,7 +1500,7 @@ def get_fills( __________ **Read more on the official documentation:** `List Fills - `_ + `_ """ endpoint = f"{API_PREFIX}/orders/historical/fills" params = { @@ -1535,7 +1537,7 @@ def edit_order( __________ **Read more on the official documentation:** `Edit Order - `_ + `_ """ endpoint = f"{API_PREFIX}/orders/edit" data = { @@ -1569,7 +1571,7 @@ def preview_edit_order( __________ **Read more on the official documentation:** `Edit Order Preview - `_ + `_ """ endpoint = f"{API_PREFIX}/orders/edit_preview" data = { @@ -1597,7 +1599,7 @@ def cancel_orders(self, order_ids: List[str], **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Cancel Orders - `_ + `_ """ endpoint = f"{API_PREFIX}/orders/batch_cancel" data = { @@ -1636,7 +1638,7 @@ def preview_order( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ endpoint = f"{API_PREFIX}/orders/preview" @@ -1690,7 +1692,7 @@ def preview_market_order( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ market_market_ioc = {"quote_size": quote_size, "base_size": base_size} @@ -1720,6 +1722,7 @@ def preview_market_order_buy( self, product_id: str, quote_size: Optional[str] = None, + base_size: Optional[str] = None, commission_rate: Optional[str] = None, is_max: Optional[bool] = False, tradable_balance: Optional[str] = None, @@ -1744,13 +1747,14 @@ def preview_market_order_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_market_order( self, product_id, "BUY", quote_size=quote_size, + base_size=base_size, commission_rate=commission_rate, is_max=is_max, tradable_balance=tradable_balance, @@ -1790,7 +1794,7 @@ def preview_market_order_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_market_order( self, @@ -1839,7 +1843,7 @@ def preview_limit_order_ioc( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ order_configuration = { "sor_limit_ioc": {"base_size": base_size, "limit_price": limit_price} @@ -1890,7 +1894,7 @@ def preview_limit_order_ioc_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_limit_order_ioc( self, @@ -1938,7 +1942,7 @@ def preview_limit_order_ioc_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_limit_order_ioc( self, @@ -1989,7 +1993,7 @@ def preview_limit_order_gtc( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ order_configuration = { "limit_limit_gtc": { @@ -2045,7 +2049,7 @@ def preview_limit_order_gtc_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_limit_order_gtc( self, @@ -2095,7 +2099,7 @@ def preview_limit_order_gtc_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_limit_order_gtc( self, @@ -2148,7 +2152,7 @@ def preview_limit_order_gtd( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ order_configuration = { "limit_limit_gtd": { @@ -2206,7 +2210,7 @@ def preview_limit_order_gtd_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_limit_order_gtd( self, @@ -2258,7 +2262,7 @@ def preview_limit_order_gtd_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_limit_order_gtd( self, @@ -2309,7 +2313,7 @@ def preview_limit_order_fok( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ order_configuration = { "limit_limit_fok": {"base_size": base_size, "limit_price": limit_price} @@ -2360,7 +2364,7 @@ def preview_limit_order_fok_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_limit_order_fok( self, @@ -2408,7 +2412,7 @@ def preview_limit_order_fok_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_limit_order_fok( self, @@ -2460,7 +2464,7 @@ def preview_stop_limit_order_gtc( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ order_configuration = { "stop_limit_stop_limit_gtc": { @@ -2518,7 +2522,7 @@ def preview_stop_limit_order_gtc_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_stop_limit_order_gtc( self, @@ -2570,7 +2574,7 @@ def preview_stop_limit_order_gtc_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_stop_limit_order_gtc( self, @@ -2625,7 +2629,7 @@ def preview_stop_limit_order_gtd( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ order_configuration = { "stop_limit_stop_limit_gtd": { @@ -2685,7 +2689,7 @@ def preview_stop_limit_order_gtd_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_stop_limit_order_gtd( self, @@ -2739,7 +2743,7 @@ def preview_stop_limit_order_gtd_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_stop_limit_order_gtd( self, @@ -2793,7 +2797,7 @@ def preview_trigger_bracket_order_gtc( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ order_configuration = { "trigger_bracket_gtc": { @@ -2849,7 +2853,7 @@ def preview_trigger_bracket_order_gtc_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_trigger_bracket_order_gtc( self, @@ -2899,7 +2903,7 @@ def preview_trigger_bracket_order_gtc_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_trigger_bracket_order_gtc( self, @@ -2952,7 +2956,7 @@ def preview_trigger_bracket_order_gtd( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ order_configuration = { "trigger_bracket_gtd": { @@ -3010,7 +3014,7 @@ def preview_trigger_bracket_order_gtd_buy( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_trigger_bracket_order_gtd( self, @@ -3062,7 +3066,7 @@ def preview_trigger_bracket_order_gtd_sell( __________ **Read more on the official documentation:** `Preview Order - `_ + `_ """ return preview_trigger_bracket_order_gtd( self, diff --git a/coinbase/rest/payments.py b/coinbase/rest/payments.py index 7dc0f41..f4b8b95 100644 --- a/coinbase/rest/payments.py +++ b/coinbase/rest/payments.py @@ -17,7 +17,7 @@ def list_payment_methods(self, **kwargs) -> Dict[str, Any]: __________ - **Read more on the official documentation:** `List Payment Methods `_ + **Read more on the official documentation:** `List Payment Methods `_ """ endpoint = f"{API_PREFIX}/payment_methods" @@ -39,7 +39,7 @@ def get_payment_method(self, payment_method_id: str, **kwargs) -> Dict[str, Any] __________ - **Read more on the official documentation:** `Get Payment Method `_ + **Read more on the official documentation:** `Get Payment Method `_ """ endpoint = f"{API_PREFIX}/payment_methods/{payment_method_id}" diff --git a/coinbase/rest/perpetuals.py b/coinbase/rest/perpetuals.py index e328fea..bb1c907 100644 --- a/coinbase/rest/perpetuals.py +++ b/coinbase/rest/perpetuals.py @@ -21,7 +21,7 @@ def allocate_portfolio( __________ **Read more on the official documentation:** `Allocate Portfolio - `_ + `_ """ endpoint = f"{API_PREFIX}/intx/allocate" @@ -52,7 +52,7 @@ def get_perps_portfolio_summary(self, portfolio_uuid: str, **kwargs) -> Dict[str __________ **Read more on the official documentation:** `Get Perpetuals Portfolio Summary - `_ + `_ """ endpoint = f"{API_PREFIX}/intx/portfolio/{portfolio_uuid}" @@ -75,7 +75,7 @@ def list_perps_positions(self, portfolio_uuid: str, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `List Perpetuals Positions - `_ + `_ """ endpoint = f"{API_PREFIX}/intx/positions/{portfolio_uuid}" @@ -100,8 +100,62 @@ def get_perps_position( __________ **Read more on the official documentation:** `Get Perpetuals Positions - `_ + `_ """ endpoint = f"{API_PREFIX}/intx/positions/{portfolio_uuid}/{symbol}" return self.get(endpoint, **kwargs) + + +def get_perps_portfolio_balances(self, portfolio_uuid: str, **kwargs) -> Dict[str, Any]: + """ + **Get Portfolio Balances** + ________________ + + [GET] https://api.coinbase.com/api/v3/brokerage/intx/balances/{portfolio_uuid} + + __________ + + **Description:** + + Get a list of asset balances on Intx for a given Portfolio + + __________ + + **Read more on the official documentation:** `Get Portfolio Balances + `_ + """ + endpoint = f"{API_PREFIX}/intx/balances/{portfolio_uuid}" + + return self.get(endpoint, **kwargs) + + +def opt_in_or_out_multi_asset_collateral( + self, portfolio_uuid: str, multi_asset_collateral_enabled: bool, **kwargs +) -> Dict[str, Any]: + """ + **Opt In or Out of Multi Asset Collateral** + ________________ + + [POST] https://api.coinbase.com/api/v3/brokerage/intx/multi_asset_collateral + + __________ + + **Description:** + + Enable or Disable Multi Asset Collateral for a given Portfolio. + + __________ + + **Read more on the official documentation:** `Opt In or Out of Multi Asset Collateral + `_ + """ + + endpoint = f"{API_PREFIX}/intx/multi_asset_collateral" + + data = { + "portfolio_uuid": portfolio_uuid, + "multi_asset_collateral_enabled": multi_asset_collateral_enabled, + } + + return self.post(endpoint, data=data, **kwargs) diff --git a/coinbase/rest/portfolios.py b/coinbase/rest/portfolios.py index be13b5d..17e3e56 100644 --- a/coinbase/rest/portfolios.py +++ b/coinbase/rest/portfolios.py @@ -21,7 +21,7 @@ def get_portfolios( __________ **Read more on the official documentation:** `List Portfolios - `_ + `_ """ endpoint = f"{API_PREFIX}/portfolios" @@ -46,7 +46,7 @@ def create_portfolio(self, name: str, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Create Portfolio - `_ + `_ """ endpoint = f"{API_PREFIX}/portfolios" @@ -73,7 +73,7 @@ def get_portfolio_breakdown(self, portfolio_uuid: str, **kwargs) -> Dict[str, An __________ **Read more on the official documentation:** `Get Portfolio Breakdown - `_ + `_ """ endpoint = f"{API_PREFIX}/portfolios/{portfolio_uuid}" @@ -103,7 +103,7 @@ def move_portfolio_funds( __________ **Read more on the official documentation:** `Move Portfolio Funds - `_ + `_ """ endpoint = f"{API_PREFIX}/portfolios/move_funds" @@ -135,7 +135,7 @@ def edit_portfolio(self, portfolio_uuid: str, name: str, **kwargs) -> Dict[str, __________ **Read more on the official documentation:** `Edit Portfolio - `_ + `_ """ endpoint = f"{API_PREFIX}/portfolios/{portfolio_uuid}" @@ -162,7 +162,7 @@ def delete_portfolio(self, portfolio_uuid: str, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Delete Portfolio - `_ + `_ """ endpoint = f"{API_PREFIX}/portfolios/{portfolio_uuid}" diff --git a/coinbase/rest/products.py b/coinbase/rest/products.py index c633ed5..e6f968d 100644 --- a/coinbase/rest/products.py +++ b/coinbase/rest/products.py @@ -28,7 +28,7 @@ def get_products( __________ **Read more on the official documentation:** `List Products - `_ + `_ """ endpoint = f"{API_PREFIX}/products" @@ -60,7 +60,7 @@ def get_product(self, product_id: str, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Get Product - `_ + `_ """ endpoint = f"{API_PREFIX}/products/{product_id}" @@ -85,7 +85,7 @@ def get_product_book( __________ **Read more on the official documentation:** `Get Product Book - `_ + `_ """ endpoint = f"{API_PREFIX}/product_book" @@ -112,7 +112,7 @@ def get_best_bid_ask( __________ **Read more on the official documentation:** `Get Best Bid/Ask - `_ + `_ """ endpoint = f"{API_PREFIX}/best_bid_ask" diff --git a/coinbase/rest/public.py b/coinbase/rest/public.py index 600148a..5f1cced 100644 --- a/coinbase/rest/public.py +++ b/coinbase/rest/public.py @@ -17,7 +17,7 @@ def get_unix_time(self, **kwargs) -> Dict[str, Any]: __________ - **Read more on the official documentation:** `Get Server Time `_ + **Read more on the official documentation:** `Get Server Time `_ """ endpoint = f"{API_PREFIX}/time" @@ -47,7 +47,7 @@ def get_public_product_book( __________ - **Read more on the official documentation:** `Get Public Product Book `_ + **Read more on the official documentation:** `Get Public Product Book `_ """ endpoint = f"{API_PREFIX}/market/product_book" @@ -88,7 +88,7 @@ def get_public_products( __________ **Read more on the official documentation:** `List Public Products - `_ + `_ """ endpoint = f"{API_PREFIX}/market/products" @@ -126,7 +126,7 @@ def get_public_product(self, product_id: str, **kwargs) -> Dict[str, Any]: __________ **Read more on the official documentation:** `Get Public Product - `_ + `_ """ endpoint = f"{API_PREFIX}/market/products/{product_id}" @@ -157,7 +157,7 @@ def get_public_candles( __________ **Read more on the official documentation:** `Get Public Product Candles - `_ + `_ """ endpoint = f"{API_PREFIX}/market/products/{product_id}/candles" @@ -199,7 +199,7 @@ def get_public_market_trades( __________ **Read more on the official documentation:** `Get Public Market Trades - `_ + `_ """ endpoint = f"{API_PREFIX}/market/products/{product_id}/ticker" diff --git a/coinbase/websocket/channels.py b/coinbase/websocket/channels.py index dfebccf..4fcd73a 100644 --- a/coinbase/websocket/channels.py +++ b/coinbase/websocket/channels.py @@ -26,7 +26,7 @@ def heartbeats(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Heartbeats Channel - `_ + `_ """ self.subscribe(product_ids, [HEARTBEATS]) @@ -45,7 +45,7 @@ async def heartbeats_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Heartbeats Channel - `_ + `_ """ await self.subscribe_async(product_ids, [HEARTBEATS]) @@ -64,7 +64,7 @@ def heartbeats_unsubscribe(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Heartbeats Channel - `_ + `_ """ self.unsubscribe(product_ids, [HEARTBEATS]) @@ -83,7 +83,7 @@ async def heartbeats_unsubscribe_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Heartbeats Channel - `_ + `_ """ await self.unsubscribe_async(product_ids, [HEARTBEATS]) @@ -102,7 +102,7 @@ def candles(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Candles Channel - `_ + `_ """ self.subscribe(product_ids, [CANDLES]) @@ -121,7 +121,7 @@ async def candles_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Candles Channel - `_ + `_ """ await self.subscribe_async(product_ids, [CANDLES]) @@ -140,7 +140,7 @@ def candles_unsubscribe(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Candles Channel - `_ + `_ """ self.unsubscribe(product_ids, [CANDLES]) @@ -159,7 +159,7 @@ async def candles_unsubscribe_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Candles Channel - `_ + `_ """ await self.unsubscribe_async(product_ids, [CANDLES]) @@ -178,7 +178,7 @@ def market_trades(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Market Trades Channel - `_ + `_ """ self.subscribe(product_ids, [MARKET_TRADES]) @@ -197,7 +197,7 @@ async def market_trades_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Market Trades Channel - `_ + `_ """ await self.subscribe_async(product_ids, [MARKET_TRADES]) @@ -216,7 +216,7 @@ def market_trades_unsubscribe(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Market Trades Channel - `_ + `_ """ self.unsubscribe(product_ids, [MARKET_TRADES]) @@ -235,7 +235,7 @@ async def market_trades_unsubscribe_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Market Trades Channel - `_ + `_ """ await self.unsubscribe_async(product_ids, [MARKET_TRADES]) @@ -254,7 +254,7 @@ def status(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Status Channel - `_ + `_ """ self.subscribe(product_ids, [STATUS]) @@ -273,7 +273,7 @@ async def status_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Status Channel - `_ + `_ """ await self.subscribe_async(product_ids, [STATUS]) @@ -292,7 +292,7 @@ def status_unsubscribe(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Status Channel - `_ + `_ """ self.unsubscribe(product_ids, [STATUS]) @@ -311,7 +311,7 @@ async def status_unsubscribe_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Status Channel - `_ + `_ """ await self.unsubscribe_async(product_ids, [STATUS]) @@ -330,7 +330,7 @@ def ticker(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Ticker Channel - `_ + `_ """ self.subscribe(product_ids, [TICKER]) @@ -349,7 +349,7 @@ async def ticker_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Ticker Channel - `_ + `_ """ await self.subscribe_async(product_ids, [TICKER]) @@ -368,7 +368,7 @@ def ticker_unsubscribe(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Ticker Channel - `_ + `_ """ self.unsubscribe(product_ids, [TICKER]) @@ -387,7 +387,7 @@ async def ticker_unsubscribe_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Ticker Channel - `_ + `_ """ await self.unsubscribe_async(product_ids, [TICKER]) @@ -406,7 +406,7 @@ def ticker_batch(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Ticker Batch Channel - `_ + `_ """ self.subscribe(product_ids, [TICKER_BATCH]) @@ -425,7 +425,7 @@ async def ticker_batch_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Ticker Batch Channel - `_ + `_ """ await self.subscribe_async(product_ids, [TICKER_BATCH]) @@ -444,7 +444,7 @@ def ticker_batch_unsubscribe(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Ticker Batch Channel - `_ + `_ """ self.unsubscribe(product_ids, [TICKER_BATCH]) @@ -463,7 +463,7 @@ async def ticker_batch_unsubscribe_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Ticker Batch Channel - `_ + `_ """ await self.unsubscribe_async(product_ids, [TICKER_BATCH]) @@ -482,7 +482,7 @@ def level2(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Level2 Channel - `_ + `_ """ self.subscribe(product_ids, [LEVEL2]) @@ -501,7 +501,7 @@ async def level2_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Level2 Channel - `_ + `_ """ await self.subscribe_async(product_ids, [LEVEL2]) @@ -520,7 +520,7 @@ def level2_unsubscribe(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Level2 Channel - `_ + `_ """ self.unsubscribe(product_ids, [LEVEL2]) @@ -539,7 +539,7 @@ async def level2_unsubscribe_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `Level2 Channel - `_ + `_ """ await self.unsubscribe_async(product_ids, [LEVEL2]) @@ -558,7 +558,7 @@ def user(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `User Channel - `_ + `_ """ self.subscribe(product_ids, [USER]) @@ -577,7 +577,7 @@ async def user_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `User Channel - `_ + `_ """ await self.subscribe_async(product_ids, [USER]) @@ -596,7 +596,7 @@ def user_unsubscribe(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `User Channel - `_ + `_ """ self.unsubscribe(product_ids, [USER]) @@ -615,6 +615,6 @@ async def user_unsubscribe_async(self, product_ids: List[str]) -> None: __________ **Read more on the official documentation:** `User Channel - `_ + `_ """ await self.unsubscribe_async(product_ids, [USER]) diff --git a/tests/rest/test_futures.py b/tests/rest/test_futures.py index f920ceb..3644839 100644 --- a/tests/rest/test_futures.py +++ b/tests/rest/test_futures.py @@ -149,3 +149,68 @@ def test_cancel_pending_futures_sweep(self): self.assertEqual(captured_request.query, "") self.assertEqual(delete, expected_response) + + def test_get_intraday_margin_setting(self): + client = RESTClient(TEST_API_KEY, TEST_API_SECRET) + + expected_response = {"key_1": "value_1", "key_2": "value_2"} + + with Mocker() as m: + m.request( + "GET", + "https://api.coinbase.com/api/v3/brokerage/cfm/intraday/margin_setting", + json=expected_response, + ) + intraday_margin_setting = client.get_intraday_margin_setting() + + captured_request = m.request_history[0] + + self.assertEqual(captured_request.query, "") + self.assertEqual(intraday_margin_setting, expected_response) + + def test_get_current_margin_window(self): + client = RESTClient(TEST_API_KEY, TEST_API_SECRET) + + expected_response = {"key_1": "value_1", "key_2": "value_2"} + + with Mocker() as m: + m.request( + "GET", + "https://api.coinbase.com/api/v3/brokerage/cfm/intraday/current_margin_window", + json=expected_response, + ) + margin_window = client.get_current_margin_window("MARGIN_PROFILE_TYPE_1") + + captured_request = m.request_history[0] + + self.assertEqual( + captured_request.query, "margin_profile_type=margin_profile_type_1" + ) + self.assertEqual(margin_window, expected_response) + + def test_set_intraday_margin_setting(self): + client = RESTClient(TEST_API_KEY, TEST_API_SECRET) + + expected_response = {"key_1": "value_1", "key_2": "value_2"} + + with Mocker() as m: + m.request( + "POST", + "https://api.coinbase.com/api/v3/brokerage/cfm/intraday/margin_setting", + json=expected_response, + ) + setting = client.set_intraday_margin_setting("setting_1") + + captured_request = m.request_history[0] + captured_json = captured_request.json() + + self.assertEqual(captured_request.query, "") + + self.assertEqual( + captured_json, + { + "setting": "setting_1", + }, + ) + + self.assertEqual(setting, expected_response) diff --git a/tests/rest/test_perpetuals.py b/tests/rest/test_perpetuals.py index 5da35a9..3343ee0 100644 --- a/tests/rest/test_perpetuals.py +++ b/tests/rest/test_perpetuals.py @@ -96,3 +96,50 @@ def test_get_perps_position(self): self.assertEqual(captured_request.query, "") self.assertEqual(portfolios, expected_response) + + def test_get_perps_portfolio_balances(self): + client = RESTClient(TEST_API_KEY, TEST_API_SECRET) + + expected_response = {"key_1": "value_1", "key_2": "value_2"} + + with Mocker() as m: + m.request( + "GET", + "https://api.coinbase.com/api/v3/brokerage/intx/balances/test_uuid", + json=expected_response, + ) + portfolios = client.get_perps_portfolio_balances(portfolio_uuid="test_uuid") + + captured_request = m.request_history[0] + + self.assertEqual(captured_request.query, "") + self.assertEqual(portfolios, expected_response) + + def test_opt_in_or_out_multi_asset_collateral(self): + client = RESTClient(TEST_API_KEY, TEST_API_SECRET) + + expected_response = {"key_1": "value_1", "key_2": "value_2"} + + with Mocker() as m: + m.request( + "POST", + "https://api.coinbase.com/api/v3/brokerage/intx/multi_asset_collateral", + json=expected_response, + ) + response = client.opt_in_or_out_multi_asset_collateral( + portfolio_uuid="test_uuid", + multi_asset_collateral_enabled=True, + ) + + captured_request = m.request_history[0] + captured_json = captured_request.json() + + self.assertEqual(captured_request.query, "") + self.assertEqual( + captured_json, + { + "portfolio_uuid": "test_uuid", + "multi_asset_collateral_enabled": True, + }, + ) + self.assertEqual(response, expected_response)