Skip to content

Commit 5bff442

Browse files
authored
Support for Portfolios Endpoints (#49)
* list portfolios * supporting create_portfolio endpoint * edit_portfolio endpoint support * delete_portfolio endpoint * get_portfolio_breakdown * move funds * modernizing python code
1 parent 09ad5c1 commit 5bff442

18 files changed

Lines changed: 2584 additions & 286 deletions

coinbaseadvanced/client.py

Lines changed: 310 additions & 87 deletions
Large diffs are not rendered by default.

coinbaseadvanced/models/accounts.py

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,13 @@
44

55
from uuid import UUID
66
from datetime import datetime
7-
from typing import List
7+
from typing import List, Optional
88
import requests
99

10-
from coinbaseadvanced.models.common import BaseModel
10+
from coinbaseadvanced.models.common import BaseModel, ValueCurrency
1111
from coinbaseadvanced.models.error import CoinbaseAdvancedTradeAPIError
1212

1313

14-
class AvailableBalance(BaseModel):
15-
"""
16-
Available Balance object.
17-
"""
18-
19-
value: str
20-
currency: str
21-
22-
def __init__(self, value: str, currency: str, **kwargs) -> None:
23-
self.value = value
24-
self.currency = currency
25-
26-
self.kwargs = kwargs
27-
28-
2914
class Account(BaseModel):
3015
"""
3116
Object representing an account.
@@ -34,15 +19,15 @@ class Account(BaseModel):
3419
uuid: UUID
3520
name: str
3621
currency: str
37-
available_balance: AvailableBalance
22+
available_balance: Optional[ValueCurrency]
3823
default: bool
3924
active: bool
4025
created_at: datetime
4126
updated_at: datetime
4227
deleted_at: datetime
4328
type: str
4429
ready: bool
45-
hold: AvailableBalance
30+
hold: Optional[ValueCurrency]
4631

4732
def __init__(
4833
self, uuid: UUID, name: str, currency: str, available_balance: dict, default: bool,
@@ -51,7 +36,7 @@ def __init__(
5136
self.uuid = uuid
5237
self.name = name
5338
self.currency = currency
54-
self.available_balance = AvailableBalance(**available_balance) \
39+
self.available_balance = ValueCurrency(**available_balance) \
5540
if available_balance is not None else None
5641
self.default = default
5742
self.active = active
@@ -60,7 +45,7 @@ def __init__(
6045
self.deleted_at = deleted_at
6146
self.type = type
6247
self.ready = ready
63-
self.hold = AvailableBalance(**hold) if hold is not None else None
48+
self.hold = ValueCurrency(**hold) if hold is not None else None
6449

6550
self.kwargs = kwargs
6651

@@ -85,19 +70,19 @@ class AccountsPage(BaseModel):
8570

8671
accounts: List[Account]
8772
has_next: bool
88-
cursor: str
73+
cursor: Optional[str]
8974
size: int
9075

9176
def __init__(self,
9277
accounts: List[dict],
9378
has_next: bool,
94-
cursor: str,
79+
cursor: Optional[str],
9580
size: int,
9681
**kwargs
9782
) -> None:
9883

9984
self.accounts = list(map(lambda x: Account(**x), accounts))\
100-
if accounts is not None else None
85+
if accounts is not None else []
10186

10287
self.has_next = has_next
10388
self.cursor = cursor
@@ -118,4 +103,4 @@ def from_response(cls, response: requests.Response) -> 'AccountsPage':
118103
return cls(**result)
119104

120105
def __iter__(self):
121-
return self.accounts.__iter__()
106+
return self.accounts.__iter__() if self.accounts is not None else [].__iter__()

coinbaseadvanced/models/common.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,63 @@
22
Object models for order related endpoints args and response.
33
"""
44

5-
from coinbaseadvanced.models.error import CoinbaseAdvancedTradeAPIError
6-
75
import requests
86

7+
from coinbaseadvanced.models.error import CoinbaseAdvancedTradeAPIError
8+
99

1010
class BaseModel:
11+
"""
12+
Base class for models.
13+
"""
1114

1215
def __str__(self):
13-
attributes = ", ".join(f"{key}={value}" for key, value in self.__dict__.items())
16+
attributes = ", ".join(
17+
f"{key}={value}" for key, value in self.__dict__.items())
1418
return f"{self.__class__.__name__}({attributes})"
1519

1620
def __repr__(self):
17-
attributes = ", ".join(f"{key}={value!r}" for key, value in self.__dict__.items())
21+
attributes = ", ".join(
22+
f"{key}={value!r}" for key, value in self.__dict__.items())
1823
return f"{self.__class__.__name__}({attributes})"
1924

2025

26+
class EmptyResponse(BaseModel):
27+
"""
28+
Represents an empty response from the Coinbase Advanced Trade API.
29+
30+
Attributes:
31+
success (bool): Indicates whether the response was successful or not.
32+
"""
33+
34+
success: bool
35+
36+
def __init__(self, **kwargs) -> None:
37+
self.success = True
38+
self.kwargs = kwargs
39+
40+
@classmethod
41+
def from_response(cls, response: requests.Response) -> 'EmptyResponse':
42+
"""
43+
Factory Method that creates an EmptyResponse object from a requests.Response object.
44+
45+
Args:
46+
response (requests.Response): The response object returned by the API.
47+
48+
Returns:
49+
EmptyResponse: An instance of the EmptyResponse class.
50+
51+
Raises:
52+
CoinbaseAdvancedTradeAPIError: If the response is not OK.
53+
"""
54+
55+
if not response.ok:
56+
raise CoinbaseAdvancedTradeAPIError.not_ok_response(response)
57+
58+
result = response.json()
59+
return cls(**result)
60+
61+
2162
class UnixTime(BaseModel):
2263
"""
2364
Unix time in different formats.
@@ -51,3 +92,18 @@ def from_response(cls, response: requests.Response) -> 'UnixTime':
5192

5293
result = response.json()
5394
return cls(**result)
95+
96+
97+
class ValueCurrency(BaseModel):
98+
"""
99+
Available Balance object.
100+
"""
101+
102+
value: str
103+
currency: str
104+
105+
def __init__(self, value: str, currency: str, **kwargs) -> None:
106+
self.value = value
107+
self.currency = currency
108+
109+
self.kwargs = kwargs

coinbaseadvanced/models/error.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def not_ok_response(cls, response: requests.Response) -> 'CoinbaseAdvancedTradeA
2626

2727
try:
2828
error_result = json.loads(response.text)
29-
except ValueError as error:
29+
except ValueError:
3030
error_result = {'reason': response.text}
3131

3232
return cls(error_dict=error_result)

coinbaseadvanced/models/fees.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Object models for fees related endpoints args and response.
33
"""
44

5+
from typing import Optional
56
import requests
67

78
from coinbaseadvanced.models.common import BaseModel
@@ -69,23 +70,33 @@ class TransactionsSummary(BaseModel):
6970

7071
total_volume: int
7172
total_fees: int
72-
fee_tier: FeeTier
73-
margin_rate: MarginRate
74-
goods_and_services_tax: GoodsAndServicesTax
73+
fee_tier: Optional[FeeTier]
74+
margin_rate: Optional[MarginRate]
75+
goods_and_services_tax: Optional[GoodsAndServicesTax]
7576
advanced_trade_only_volume: int
7677
advanced_trade_only_fees: int
7778
coinbase_pro_volume: int
7879
coinbase_pro_fees: int
7980
total_balance: str
8081
has_promo_fee: bool
8182

82-
def __init__(self, total_volume: int, total_fees: int, fee_tier: dict, margin_rate: dict,
83-
goods_and_services_tax: dict, advanced_trade_only_volume: int, advanced_trade_only_fees: int,
84-
coinbase_pro_volume: int, coinbase_pro_fees: int, total_balance: str, has_promo_fee: bool, **kwargs) -> None:
83+
def __init__(self,
84+
total_volume: int,
85+
total_fees: int,
86+
fee_tier: dict,
87+
margin_rate: dict,
88+
goods_and_services_tax: dict,
89+
advanced_trade_only_volume: int,
90+
advanced_trade_only_fees: int,
91+
coinbase_pro_volume: int,
92+
coinbase_pro_fees: int,
93+
total_balance: str,
94+
has_promo_fee: bool, **kwargs) -> None:
8595
self.total_volume = total_volume
8696
self.total_fees = total_fees
8797
self.fee_tier = FeeTier(**fee_tier) if fee_tier is not None else None
88-
self.margin_rate = MarginRate(**margin_rate) if margin_rate is not None else None
98+
self.margin_rate = MarginRate(
99+
**margin_rate) if margin_rate is not None else None
89100
self.goods_and_services_tax = GoodsAndServicesTax(
90101
**goods_and_services_tax) if goods_and_services_tax is not None else None
91102
self.advanced_trade_only_volume = advanced_trade_only_volume

coinbaseadvanced/models/futures.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""
2+
This module contains the definition of the FuturesPosition class and related enums.
3+
"""
4+
5+
from enum import Enum
6+
from coinbaseadvanced.models.common import BaseModel
7+
8+
9+
class MarginType(Enum):
10+
"""
11+
Enum representing the margin type for futures trading.
12+
"""
13+
14+
UNSPECIFIED = "MARGIN_TYPE_UNSPECIFIED"
15+
CROSS = "MARGIN_TYPE_CROSS"
16+
ISOLATED = "MARGIN_TYPE_ISOLATED"
17+
18+
19+
class FuturesPositionSide(Enum):
20+
"""
21+
Enum representing the position side for futures contracts.
22+
"""
23+
24+
UNSPECIFIED = "FUTURES_POSITION_SIDE_UNSPECIFIED"
25+
LONG = "FUTURES_POSITION_SIDE_LONG"
26+
SHORT = "FUTURES_POSITION_SIDE_SHORT"
27+
28+
29+
class FuturesPosition(BaseModel):
30+
"""
31+
Represents a futures position.
32+
33+
Attributes:
34+
product_id (str): The ID of the product.
35+
contract_size (str): The size of the contract.
36+
side (FuturesPositionSide): The side of the position.
37+
amount (str): The amount of the position.
38+
avg_entry_price (str): The average entry price of the position.
39+
current_price (str): The current price of the position.
40+
unrealized_pnl (str): The unrealized profit/loss of the position.
41+
expiry (str): The expiry date of the position.
42+
underlying_asset (str): The underlying asset of the position.
43+
asset_img_url (str): The URL of the asset's image.
44+
product_name (str): The name of the product.
45+
venue (str): The venue of the position.
46+
notional_value (str): The notional value of the position.
47+
"""
48+
49+
def __init__(
50+
self, product_id: str,
51+
contract_size: str,
52+
side: str,
53+
amount: str,
54+
avg_entry_price: str,
55+
current_price: str,
56+
unrealized_pnl: str,
57+
expiry: str,
58+
underlying_asset: str,
59+
asset_img_url: str,
60+
product_name: str,
61+
venue: str,
62+
notional_value: str, **kwargs) -> None:
63+
self.product_id = product_id
64+
self.contract_size = contract_size
65+
self.side = FuturesPositionSide[side]
66+
self.amount = amount
67+
self.avg_entry_price = avg_entry_price
68+
self.current_price = current_price
69+
self.unrealized_pnl = unrealized_pnl
70+
self.expiry = expiry
71+
self.underlying_asset = underlying_asset
72+
self.asset_img_url = asset_img_url
73+
self.product_name = product_name
74+
self.venue = venue
75+
self.notional_value = notional_value
76+
77+
self.kwargs = kwargs

0 commit comments

Comments
 (0)