Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.2.2 #40

Merged
merged 2 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [1.2.2] - 2024-APR-9

### Added
- Support for ClosePosition endpoint

### Changed
- Audience no longer included in JWT generation

## [1.2.1] - 2024-MAR-27

### Added
Expand Down
2 changes: 1 addition & 1 deletion coinbase/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.2.1"
__version__ = "1.2.2"
7 changes: 3 additions & 4 deletions coinbase/jwt_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from coinbase.constants import BASE_URL, REST_SERVICE, WS_SERVICE


def build_jwt(key_var, secret_var, service, uri=None) -> str:
def build_jwt(key_var, secret_var, uri=None) -> str:
"""
:meta private:
"""
Expand All @@ -28,7 +28,6 @@ def build_jwt(key_var, secret_var, service, uri=None) -> str:
"iss": "coinbase-cloud",
"nbf": int(time.time()),
"exp": int(time.time()) + 120,
"aud": [service],
}

if uri:
Expand Down Expand Up @@ -61,7 +60,7 @@ def build_rest_jwt(uri, key_var, secret_var) -> str:
- **key_var (str)** - The API key
- **secret_var (str)** - The API key secret
"""
return build_jwt(key_var, secret_var, REST_SERVICE, uri=uri)
return build_jwt(key_var, secret_var, uri=uri)


def build_ws_jwt(key_var, secret_var) -> str:
Expand All @@ -80,7 +79,7 @@ def build_ws_jwt(key_var, secret_var) -> str:
- **key_var (str)** - The API key
- **secret_var (str)** - The API key secret
"""
return build_jwt(key_var, secret_var, WS_SERVICE)
return build_jwt(key_var, secret_var)


def format_jwt_uri(method, path) -> str:
Expand Down
1 change: 1 addition & 0 deletions coinbase/rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class RESTClient(RESTBase):
from .fees import get_transaction_summary
from .futures import (
cancel_pending_futures_sweep,
close_position,
get_futures_balance_summary,
get_futures_position,
list_futures_positions,
Expand Down
28 changes: 27 additions & 1 deletion coinbase/rest/futures.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
from typing import Any, Dict
from typing import Any, Dict, Optional

from coinbase.constants import API_PREFIX


def close_position(
self, client_order_id: str, product_id: str, size: Optional[str] = None, **kwargs
) -> Dict[str, Any]:
"""
**Close Position**
_________________

[POST] https://api.coinbase.com/api/v3/brokerage/orders/close_position

__________

**Description:**

Places an order to close any open positions for a specified ``product_id``.

__________

**Read more on the official documentation:** `Close Position
<https://docs.cloud.coinbase.com/advanced-trade-api/reference/retailbrokerageapi_closeposition>`_
"""
endpoint = f"{API_PREFIX}/orders/close_position"
data = {"client_order_id": client_order_id, "product_id": product_id, "size": size}

return self.post(endpoint, data=data, **kwargs)


def get_futures_balance_summary(self, **kwargs) -> Dict[str, Any]:
"""
**Get Futures Balance Summary**
Expand Down
32 changes: 32 additions & 0 deletions tests/rest/test_futures.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,38 @@


class FuturesTest(unittest.TestCase):
def test_close_position(self):
client = RESTClient(TEST_API_KEY, TEST_API_SECRET)

expected_response = {
"client_order_id": "client_order_id_1",
"product_id": "product_id_1",
}

with Mocker() as m:
m.request(
"POST",
"https://api.coinbase.com/api/v3/brokerage/orders/close_position",
json=expected_response,
)
closedOrder = client.close_position(
"client_order_id_1", "product_id_1", "100"
)

captured_request = m.request_history[0]
captured_json = captured_request.json()

self.assertEqual(captured_request.query, "")
self.assertEqual(
captured_json,
{
"client_order_id": "client_order_id_1",
"product_id": "product_id_1",
"size": "100",
},
)
self.assertEqual(closedOrder, expected_response)

def test_get_futures_balance_summary(self):
client = RESTClient(TEST_API_KEY, TEST_API_SECRET)

Expand Down
10 changes: 2 additions & 8 deletions tests/test_jwt_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,24 @@ def test_build_rest_jwt(self):
uri = jwt_generator.format_jwt_uri("GET", "/api/v3/brokerage/accounts")
result_jwt = jwt_generator.build_rest_jwt(uri, TEST_API_KEY, TEST_API_SECRET)

decoded_data = jwt.decode(
result_jwt, TEST_API_SECRET, algorithms=["ES256"], audience=[REST_SERVICE]
)
decoded_data = jwt.decode(result_jwt, TEST_API_SECRET, algorithms=["ES256"])
header_bytes = base64.urlsafe_b64decode(str(result_jwt.split(".")[0] + "=="))
decoded_header = json.loads(header_bytes.decode("utf-8"))

self.assertEqual(decoded_data["sub"], TEST_API_KEY)
self.assertEqual(decoded_data["iss"], "coinbase-cloud")
self.assertEqual(decoded_data["aud"], [REST_SERVICE])
self.assertEqual(decoded_data["uri"], uri)
self.assertEqual(decoded_header["kid"], TEST_API_KEY)

def test_build_ws_jwt(self):
result_jwt = jwt_generator.build_ws_jwt(TEST_API_KEY, TEST_API_SECRET)

decoded_data = jwt.decode(
result_jwt, TEST_API_SECRET, algorithms=["ES256"], audience=[WS_SERVICE]
)
decoded_data = jwt.decode(result_jwt, TEST_API_SECRET, algorithms=["ES256"])
header_bytes = base64.urlsafe_b64decode(str(result_jwt.split(".")[0] + "=="))
decoded_header = json.loads(header_bytes.decode("utf-8"))

self.assertEqual(decoded_data["sub"], TEST_API_KEY)
self.assertEqual(decoded_data["iss"], "coinbase-cloud")
self.assertEqual(decoded_data["aud"], [WS_SERVICE])
self.assertNotIn("uri", decoded_data)
self.assertEqual(decoded_header["kid"], TEST_API_KEY)

Expand Down
Loading