A Python 3 Client Wrapper for the Coinbase Pro Rest API
-
Requires Python 3.6 or greater
-
Provided under MIT License by Daniel Paquin.
- A simple to use python wrapper for both public and authenticated endpoints.
- In about 10 minutes, you could be programmatically trading on one of the largest Bitcoin exchanges in the world!
- Do not worry about handling the nuances of the API with easy-to-use methods for every API endpoint.
- Gain an advantage in the market by getting under the hood of CB Pro to learn what and who is behind every tick.
- Test Scripts
- Real-Time Order Book
- Web Socket Client
- FIX API Client Looking for assistance
-
NOTE: This library may be subtly broken or buggy.
-
NOTE: This library is a fork of the original. This library will resemble the original less over time as development continues. The API is not compatible with the original and will break your client interface. If you are here looking for the original GDAX project, you can find it here. I have left the the original
LICENSEandcontributors.txtfiles to credit the original author as well as the projects contributors.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This README.md is documentation on the syntax of the python client presented in
this repository.
See both Requests and Coinbase Pro API Documentation for full details.
WARNING: It's recommended that you use the websocket interface instead of polling the general interface methods. It's best to reference the Coinbase Pro API Docs to see where polling is allowed, or even encouraged, in some niche cases. Polling can result in blocking, or even banning, access to the API in most other cases.
NOTE: This library conflicts with the original coinbasepro-python package. Make sure to remove it before installing this package to avoid package conflicts.
- Install manually
git clone https://github.com/teleprint-me/coinbasepro-python.git- Install with
pip
pip install git+git://github.com/teleprint-me/coinbasepro-python.gitcbpro.auth.Auth(key: str, secret: str, passphrase: str)Use the Auth object to authenticate yourself with private endpoints. The Auth object is a callable object that is passed to a requests method.
Example:
import cbpro
import requests
key = 'My Key'
secret = 'My Secret'
passphrase = 'My Passphrase'
sandbox = 'https://api-public.sandbox.pro.coinbase.com'
endpoint = '/accounts'
url = sandbox + endpoint
params = None
auth = cbpro.Auth(key, secret, passphrase)
response = requests.get(
url,
params=params,
auth=auth,
timeout=30
)
json = response.json()
print(json)class cbpro.messenger.Messenger(auth: cbpro.auth.Auth = None,
url: str = None,
timeout: int = None) The Messenger object is a requests wrapper. It handles most of the common repeated tasks for you.
The Messenger object methods will return a dict in most cases. Methods may also return a list. The Messenger.paginate method returns a generator.
The Messenger object defaults to using the Rest API URL. It is recommended to use the Sandbox Rest API URL instead for testing.
Example:
import cbpro
key = 'My Key'
secret = 'My Secret'
passphrase = 'My Passphrase'
auth = cbpro.Auth(key, secret, passphrase)
sandbox = 'https://api-public.sandbox.pro.coinbase.com'
endpoint = '/accounts'
messenger = cbpro.Messenger(auth=auth, url=sandbox)
accounts = messenger.get(endpoint)
print(accounts)messenger.paginate(endpoint: str, params: dict = None) -> objectThe Messenger interface provides a method for paginated endpoints. Multiple calls must be made to receive the full set of data.
Messenger.paginate returns a generator which provides a clean interface for iteration and may make multiple requests behind the scenes.
The pagination options before, after, and limit may be supplied as keyword arguments if desired and are not necessary for typical use cases.
Example:
import cbpro
key = 'My Key'
secret = 'My Secret'
passphrase = 'My Passphrase'
sandbox = 'https://api-public.sandbox.pro.coinbase.com'
endpoint = '/fills'
auth = cbpro.Auth(key, secret, passphrase)
messenger = cbpro.Messenger(auth=auth, url=sandbox)
params = {'product_id': 'BTC-USD', 'before': 2}
fills = messenger.paginate(endpoint, params)
for fill in fills:
print(fill)A Messenger instance is passed to the PublicClient or PrivateClient objects and then shared among the related classes during instantiation of client related objects. Each instance has its own memory and shares a reference to the same Messenger instance.
Any objects that subscribe to the Subscriber interface inherit the initialization of the Subscriber.messenger property allowing them to effectively communicate via the same Auth and requests.Session objects without duplicating code.
# ...
class Time(cbpro.messenger.Subscriber):
def get(self) -> dict:
return self.messenger.get('/time')
class PublicClient(object):
def __init__(self, messenger: cbpro.messenger.Messenger) -> None:
self.products = Products(messenger)
self.currencies = Currencies(messenger)
self.time = Time(messenger)
# ...cbpro.public.PublicClient(messenger: cbpro.messenger.Messenger)Only some endpoints in the API are available to everyone. The public endpoints
can be reached using cbpro.public.PublicClient object.
Example 1:
import cbpro
messenger = cbpro.Messenger()
public = cbpro.PublicClient(messenger)cbpro.public.public_client(url: str = None)You can use the public_client method in most cases to simplify instantiation. Example 2 is only one line of code where Example 1 is 2 lines of code. They both return an instantiated PublicClient object either way.
Example 2:
import cbpro
public = cbpro.public_client()cbpro.public.Products(messenger: cbpro.messenger.Messenger)public.products.list() -> listExample:
products = public.products.list()
type(products) # <class 'list'>
len(products) # 193public.products.get(product_id: str) -> dictExample:
product_id = 'BTC-USD'
product = public.products.get(product_id)
type(product) # <class 'dict'>
print(product)# NOTE:
# - Polling is discouraged for this method
# - Use the websocket stream for polling instead
public.products.order_book(product_id: str, params: dict) -> dictExample:
params = {'level': 1}
book = public.products.order_book(product_id, params)
type(book) # <class 'dict'>
print(book)# NOTE:
# - Polling is discouraged for this method
# - Use the websocket stream for polling instead
public.product.ticker(product_id: str) -> dict# NOTE:
# - This request is paginated
public.products.trades(product_id: str, params: dict = None) -> objectExample:
params = {'before': 2}
trades = public.products.trades(product_id, params)
type(trades) # <class 'generator'>
print(trades) # <generator object Messenger.paginate at 0x7f9b0ac24cf0>
for index, trade in enumerate(trades):
if index == 10:
break
print(trade)# NOTE:
# - Polling is discouraged for this method
# - Use the websocket stream for polling instead
public.products.history(product_id: str, params: dict) -> listExample:
import datetime
start = datetime.datetime(2021, 4, 1, 0, 0, 0, 0).isoformat()
end = datetime.datetime(2021, 4, 7, 0, 0, 0, 0).isoformat()
day = 86400
params = {'start': start, 'end': end, 'granularity': day}
history = public.products.history(product_id, params)
type(history) # <class 'list'>
len(history) # 7
print(history)public.products.stats(product_id: str) -> dictcbpro.public.Currencies(messenger: cbpro.messenger.Messenger)public.currencies.list() -> listpublic.currencies.get(product_id: str) -> dictcbpro.public.Time(messenger: cbpro.messenger.Messenger)public.time.get() -> dictcbpro.models.PublicModel()Use PublicModel to generate passable parameters easily.
Models will help enforce your code according to what the API expects. If a parameter is incorrect, or forgotten, then the model will raise an AssertionError.
This helps in seperating the application logic from the client interface leaving the client objects clean and tidy.
Example:
import cbpro
model = cbpro.PublicModel()cbpro.models.ProductsModel()model.products.order_book(level: int = None) -> dictExample 1:
try: # intentionally trigger
params = model.products.order_book(5)
except (AssertionError,) as e:
print('AssertionError:', e)
# AssertionError: `level` must be one of: [1, 2, 3]Example 2:
params = model.products.order_book(2)
print(params) # {'level': 2}
book = public.products.order_book(product_id, params=params)
type(book) # <class 'dict'>
print(book)model.products.history(start: str = None,
end: str = None,
granularity: int = 86400) -> dictExample:
import datetime
start = datetime.datetime(2021, 4, 1, 0, 0, 0, 0).isoformat()
end = datetime.datetime(2021, 4, 7, 0, 0, 0, 0).isoformat()
params = model.products.history(start, end)
print(params)
# {'start': '2021-04-01T00:00:00', 'end': '2021-04-07T00:00:00', 'granularity': 86400}
history = public.products.history(product_id, params)
type(history) # <class 'list'>
len(history) # 7
print(history)cbpro.private.PrivateClient(messenger: cbpro.messenger.Messenger)Not all API endpoints are available to everyone.
Those requiring user authentication can be reached using the PrivateClient object. You must setup API access within your
Account Settings.
NOTE: The PrivateClient object inherits from the PublicClient object.
Example 1:
import cbpro
key = 'My Key'
secret = 'My Secret'
passphrase = 'My Passphrase'
sandbox = 'https://api-public.sandbox.pro.coinbase.com'
auth = cbpro.Auth(key, secret, passphrase)
messenger = cbpro.Messenger(auth=auth, url=sandbox)
private = cbpro.PrivateClient(messenger)cbpro.private.private_client(key: str,
secret: str,
passphrase: str,
url: str = None) -> PrivateClient:You can use the private_client method in most cases to simplify instantiation. Example 2 is only one line of code where Example 1 is 3 lines of code. They both return an instantiated PrivateClient object either way.
Example 2:
import cbpro
key = 'My Key'
secret = 'My Secret'
passphrase = 'My Passphrase'
sandbox = 'https://api-public.sandbox.pro.coinbase.com'
private = cbpro.private_client(key, secret, passphrase, sandbox)cbpro.private.Accounts(messenger: cbpro.messenger.Messenger)private.accounts.list() -> listprivate.accounts.get(account_id: str) -> dict# NOTE:
# - This request is paginated
private.accounts.history(account_id: str, params: dict = None) -> list# NOTE:
# - This request is paginated
private.accounts.holds(account_id: str, params: dict = None) -> listcbpro.private.Orders(messenger: cbpro.messenger.Messenger)private.orders.post(json: dict) -> dictExample: Limit Order
limit = {
'side': 'buy',
'product_id': 'BTC-USD',
'type': 'limit',
'price': 57336.2,
'size': 0.001
}
private.orders.post(limit)Example: Market Order
market = {
'side': 'buy',
'product_id': 'BTC-USD',
'type': 'market',
'funds': 100.0
}
private.orders.post(market)Example: Limit Stop Order
stop = {
'side': 'buy',
'product_id': 'BTC-USD',
'type': 'limit',
'stop': 'loss',
'stop_price': 50000.0,
'price': 57064.8,
'size': 0.001
}
private.orders.post(stop)Example: Market Stop Order
stop = {
'side': 'buy',
'product_id': 'BTC-USD',
'type': 'market',
'stop': 'loss',
'stop_price': 45000.0,
'funds': 100.0
}
private.orders.post(stop)private.orders.cancel(id_: str, params: dict = None) -> listprivate.orders.cancel_client(oid: str, params: dict = None) -> listprivate.orders.cancel_all(params: dict = None) -> list# NOTE:
# - This request is paginated
private.orders.list(params: dict) -> listprivate.orders.get(id_: str) -> dictprivate.orders.get_client(oid: str) -> dictcbpro.private.Fills(messenger: cbpro.messenger.Messenger)# NOTE:
# - This request is paginated
# - You are required to provide either a `product_id` or `order_id`
private.fills.list(params: dict) -> listExample 1:
product_id = {'product_id': 'BTC-USD'}
private.fills.list(product_id)Example 2:
order_id = {'order_id': '0e953c31-9bce-4007-978c-302be337b566'}
private.fills.list(order_id)cbpro.private.Limits(messenger: cbpro.messenger.Messenger)private.limits.get() -> dictcbpro.private.Deposits(messenger: cbpro.messenger.Messenger)# NOTE:
# - This request is paginated
private.deposits.list(params: dict = None) -> listprivate.deposits.get(transfer_id: str) -> dictprivate.deposits.payment(json: dict) -> dictprivate.deposits.coinbase(json: dict) -> dictprivate.deposits.generate(account_id: str) -> dict# NOTE:
# - `cbpro.private.Withdrawls` inherits from `cbpro.private.Deposits`
cbpro.private.Withdrawals(messenger: cbpro.messenger.Messenger)# NOTE:
# - This method is overridden
private.withdrawals.payment(json: dict) -> dict# NOTE:
# - This method is overridden
private.withdrawals.coinbase(json: dict) -> dictprivate.withdrawals.crypto(json: dict) -> dictprivate.withdrawals.estimate(params: dict) -> dictcbpro.private.Conversions(messenger: cbpro.messenger.Messenger)private.conversions.create(json: dict) -> dictcbpro.private.Payments(messenger: cbpro.messenger.Messenger)private.payments.list() -> listcbpro.private.Coinbase(messenger: cbpro.messenger.Messenger)private.coinbase.list() -> listcbpro.private.Fees(messenger: cbpro.messenger.Messenger)private.fees.get() -> list# NOTE:
# - This object is currently undefined
cbpro.private.Reports(messenger: cbpro.messenger.Messenger)cbpro.private.Profiles(messenger: cbpro.messenger.Messenger)private.profiles.list(params: dict = None) -> listprivate.profiles.get(profile_id: str) -> dictprivate.profiles.transfer(json: dict) -> dict# NOTE:
# - This object is currently undefined
cbpro.private.Oracle(messenger: cbpro.messenger.Messenger)If you would like to receive real-time market updates, you must subscribe to the websocket feed.
import cbpro
# Parameters are optional
wsClient = cbpro.WebsocketClient(url="wss://ws-feed.pro.coinbase.com",
products="BTC-USD",
channels=["ticker"])
# Do other stuff...
wsClient.close()import cbpro
# Parameters are optional
wsClient = cbpro.WebsocketClient(url="wss://ws-feed.pro.coinbase.com",
products=["BTC-USD", "ETH-USD"],
channels=["ticker"])
# Do other stuff...
wsClient.close()The WebsocketClient now supports data gathering via MongoDB. Given a
MongoDB collection, the WebsocketClient will stream results directly into
the database collection.
# import PyMongo and connect to a local, running Mongo instance
from pymongo import MongoClient
import cbpro
mongo_client = MongoClient('mongodb://localhost:27017/')
# specify the database and collection
db = mongo_client.cryptocurrency_database
BTC_collection = db.BTC_collection
# instantiate a WebsocketClient instance, with a Mongo collection as a parameter
wsClient = cbpro.WebsocketClient(url="wss://ws-feed.pro.coinbase.com", products="BTC-USD",
mongo_collection=BTC_collection, should_print=False)
wsClient.start()The WebsocketClient subscribes in a separate thread upon initialization.
There are three methods which you could overwrite (before initialization) so it
can react to the data streaming in. The current client is a template used for
illustration purposes only.
- onOpen - called once, immediately before the socket connection is made, this is where you want to add initial parameters.
- onMessage - called once for every message that arrives and accepts one argument that contains the message of dict type.
- on_close - called once after the websocket has been closed.
- close - call this method to close the websocket connection (do not overwrite).
import cbpro, time
class myWebsocketClient(cbpro.WebsocketClient):
def on_open(self):
self.url = "wss://ws-feed.pro.coinbase.com/"
self.products = ["LTC-USD"]
self.message_count = 0
print("Lets count the messages!")
def on_message(self, msg):
self.message_count += 1
if 'price' in msg and 'type' in msg:
print ("Message type:", msg["type"],
"\t@ {:.3f}".format(float(msg["price"])))
def on_close(self):
print("-- Goodbye! --")
wsClient = myWebsocketClient()
wsClient.start()
print(wsClient.url, wsClient.products)
while (wsClient.message_count < 500):
print ("\nmessage_count =", "{} \n".format(wsClient.message_count))
time.sleep(1)
wsClient.close()A test suite is under development. Tests for the authenticated client require a
set of sandbox API credentials. To provide them, rename
api_config.json.example in the tests folder to api_config.json and edit the
file accordingly. To run the tests, start in the project directory and run
python -m pytest
The OrderBook subscribes to a websocket and keeps a real-time record of
the orderbook for the product_id input. Please provide your feedback for future
improvements.
import cbpro, time
order_book = cbpro.OrderBook(product_id='BTC-USD')
order_book.start()
time.sleep(10)
order_book.close()Unit tests are under development using the pytest framework. Contributions are welcome!
To run the full test suite, in the project directory run:
python -m pytest1.1.2 Current PyPI release
- Refactor project for Coinbase Pro
- Major overhaul on how pagination is handled
1.0
- The first release that is not backwards compatible
- Refactored to follow PEP 8 Standards
- Improved Documentation
0.3
- Added crypto and LTC deposit & withdraw (undocumented).
- Added support for Margin trading (undocumented).
- Enhanced functionality of the WebsocketClient.
- Soft launch of the OrderBook (undocumented).
- Minor bug squashing & syntax improvements.
0.2.2
- Added additional API functionality such as cancelAll() and ETH withdrawal.
0.2.1
- Allowed
WebsocketClientto operate intuitively and restructured example workflow.
0.2.0
- Renamed project to GDAX-Python
- Merged Websocket updates to handle errors and reconnect.
0.1.2
- Updated JSON handling for increased compatibility among some users.
- Added support for payment methods, reports, and Coinbase user accounts.
- Other compatibility updates.
0.1.1b2
- Original PyPI Release.