Source code for copra.rest.client

# -*- coding: utf-8 -*-
"""Public (unauthenticated) and authenticated asyncronous REST client 
for the Coinbase Pro platform.

"""

import asyncio
import base64
from datetime import datetime, timedelta
import hashlib
import hmac
import json
import sys
import time
import urllib.parse

import aiohttp
import dateutil.parser
from multidict import CIMultiDict

from copra import __version__

URL = 'https://api.pro.coinbase.com'
SANDBOX_URL = 'https://api-public.sandbox.pro.coinbase.com'

USER_AGENT = 'Python/{} copra/{}'.format(
                '.'.join([str(x) for x in sys.version_info[:3]]), __version__)
                
HEADERS = {'USER-AGENT': USER_AGENT}


class APIRequestError(Exception):
    """Error returned by the server to an API endpoint request.
    
    These errors are generated by the Coinbase Pro API server. They cover isses
    such as invalid API key, forbidden access to resources, internal server
    errors, etc. The string representation of the error will display the message
    the server provided. The response field is the aiohttp.ClientResponse 
    object returned by the aiohttp request. It can be inspected to get more 
    detail including the HTTP status code, response headers, etc.
    
    :ivar aiohttp.ClientResponse: The ClientResponse object returned by the
        aiohttp request that caused the error.
    """
    def __init__(self, message, response):
        super().__init__(message)
        self.response = response
    
    
[docs]class Client: """Asyncronous REST client for Coinbase Pro. .. _pagination: .. admonition:: About Pagination Most (but not all) client methods that return a list use cursor pagination. Cursor pagination allows for fetching results before and after the current page of results and is well suited for realtime data. While non-paginated methods return a single value, paginated methods return a 3-tuple consisting of the page of results, the **before** cursor, and the **after** cursor. The **before** cursor references the first item in a results page and the **after** cursor references the last item in a set of results. To request a page of results *before* the current one (*newer chronologically*), use the **before** parameter. Your initial request can omit this parameter to get the default first page. To request a page of results *after* the current one (*older chronologically*), use the **after** parameter. """
[docs] def __init__(self, loop, url=URL, auth=False, key='', secret='', passphrase=''): """ :param loop: The asyncio loop that the client runs in. :type loop: asyncio loop :param str url: (optional) The REST API server url. The default is https://api.pro.coinbase.com. This generally does not need to be changed. One reason to change it would be to test against the sandbox server. :param bool auth: (optional) Whether or not the (entire) REST session is authenticated. If True, you will need an API key from the Coinbase Pro website. The default is False. :param str key: (optional) The API key to use for authentication. Required if auth is True. The default is ''. :param str secret: (optional) The secret string for the API key used for authenticaiton. Required if auth is True. The default is ''. :param str passphrase: (optional) The passphrase for the API key used for authentication. Required if auth is True. The default is ''. :raises ValueError: If auth is True and key, secret, and passphrase are not provided. """ self.loop = loop self.url = url if auth and not (key and secret and passphrase): raise ValueError('auth requires key, secret, and passphrase') self.auth = auth self.key = key self.secret = secret self.passphrase = passphrase self.session = aiohttp.ClientSession(loop=loop)
@property def closed(self): """True if the client has been closed, False otherwise """ return self.session.closed
[docs] async def close(self): """Close the client session and release all aquired resources. """ await self.session.close()
async def __aenter__(self): return self async def __aexit__(self, exc_type, exc_value, traceback): await self.session.close() def _get_auth_headers(self, path, method='GET', data='', timestamp=None): """Get the headers necessary to authenticate a client request. :param str path: The path portion of the REST request. For example, '/products/BTC-USD/candles' :param str method: (optional) The method of the request. The default is GET. :param json data: (optional) json-encoded dict or Multidict of key/value str pairs to be sent as the body of a POST request. The default is ''. :param float timestamp: (optional) A UNIX timestamp. This parameter exists for testing purposes and generally should not be used. If a timestamp is provided it must be within 30 seconds of the API server's time. This can be found using: :meth:`copra.rest.Client.server_time`. :returns: A dict of headers to be added to the request. :raises ValueError: auth is not True. """ if not self.auth: raise ValueError('client is not properly configured for authorization') if not timestamp: timestamp = time.time() timestamp = str(timestamp) message = timestamp + method + path + data message = message.encode('ascii') hmac_key = base64.b64decode(self.secret) signature = hmac.new(hmac_key, message, hashlib.sha256) signature_b64 = base64.b64encode(signature.digest()).decode('utf-8') return { 'USER-AGENT': USER_AGENT, 'Content-Type': 'Application/JSON', 'CB-ACCESS-SIGN': signature_b64, 'CB-ACCESS-TIMESTAMP': timestamp, 'CB-ACCESS-KEY': self.key, 'CB-ACCESS-PASSPHRASE': self.passphrase } async def _handle_error(self, response): """Handle http request errors. This method is called whenever an API call returns an HTTP status code of 400 or higher. :param aiohttp.ClientResponse response: the response returned by the aiohttp request call. """ if response.content_type.lower() == 'text/html': msg = await response.text() else: msg = (await response.json())['message'] msg += ' [{}]'.format(response.status) raise APIRequestError(msg, response)
[docs] async def delete(self, path='/', params=None, auth=False): """Base method for making DELETE requests. :param str path: (optional) The path not including the base URL of the resource to be deleted. The default is '/'. :param dict params: (optional) dict or MultiDict of key/value str pairs to be appended as the request's query string. The default is None. :param boolean auth: (optional) Indicates whether or not this request needs to be authenticated. The default is False. :returns: A 2-tuple: (response headers, response body). Response headers is a dict with the HTTP headers of the response. The response body is a JSON-formatted, UTF-8 encoded str. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ # Coinbase doesn't like ':' urlencoded qs = '?{}'.format(urllib.parse.urlencode(params, safe=':')) if params else '' url = self.url + path + qs req_headers = self._get_auth_headers(path + qs, 'DELETE') if auth else HEADERS resp = await self.session.delete(url, headers=req_headers) if int(resp.status) >= 400: await self._handle_error(resp) body = await resp.json() headers = dict(resp.headers) return (headers, body)
[docs] async def get(self, path='/', params=None, auth=False): """Base method for making GET requests. :param str path: (optional) The path not including the base URL of the resource to be retrieved. The default is '/'. :param dict params: (optional) dict or MultiDict of key/value str pairs to be appended as the request's query string. The default is None. :param boolean auth: (optional) Indicates whether or not this request needs to be authenticated. The default is False. :returns: A 2-tuple: (response headers, response body). Response headers is a dict with the HTTP headers of the response. The response body is a JSON-formatted, UTF-8 encoded str. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if not params: params = {} # Add a timestamp paramater to the query string to ensure that the # REST server returns a fresh and not cached response. if 'no-cache' not in params: params['no-cache'] = str(time.time()) # Coinbase doesn't like ':' urlencoded qs = '?{}'.format(urllib.parse.urlencode(params, safe=':')) if params else '' url = self.url + path + qs req_headers = self._get_auth_headers(path + qs) if auth else HEADERS resp = await self.session.get(url, headers=req_headers) if int(resp.status) >= 400: await self._handle_error(resp) body = await resp.json() headers = dict(resp.headers) return (headers, body)
[docs] async def post(self, path='/', data=None, auth=False): """Base method for making POST requests. :param str path: (optional) The path not including the base URL of the resource to be POST'ed to. The default is '/' :param dict data: (optional) Dictionary of key/value str pairs to be sent in the body of the request. The default is None. :param boolean auth: (optional) Indicates whether or not this request needs to be authenticated. The default is False. :returns: A 2-tuple: (response headers, response body). Response headers is a dict with the HTTP headers of the response. The response body is a JSON-formatted, UTF-8 encoded str. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ data = json.dumps(data) if data else '' url = self.url + path req_headers = self._get_auth_headers(path, 'POST', data) if auth else HEADERS resp = await self.session.post(url, data=data, headers=req_headers) if int(resp.status) >= 400: await self._handle_error(resp) body = await resp.json() headers = dict(resp.headers) return (headers, body)
[docs] async def products(self): """Get a list of available currency pairs for trading. The base_min_size and base_max_size fields define the min and max order size. The quote_increment field specifies the min order price as well as the price increment. The order price must be a multiple of this increment (i.e. if the increment is 0.01, order prices of 0.001 or 0.021 would be rejected). .. note:: Product ID will not change once assigned to a product but the min/max/quote sizes can be updated in the future. :returns: A list of dicts representing the currency pairs available for trading. Example:: [ { 'id': 'BTC-USD', 'base_currency': 'BTC', 'quote_currency': 'USD', 'base_min_size': '0.001', 'base_max_size': '70', 'quote_increment': '0.01', 'display_name': 'BTC/USD', 'status': 'online', 'margin_enabled': False, 'status_message': None, 'min_market_funds': '10', 'max_market_funds': '1000000', 'post_only': False, 'limit_only': False, 'cancel_only': False }, ... ] :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/products') return body
[docs] async def order_book(self, product_id, level=1): """Get a list of open orders for a product. By default, only the inside (i.e. best) bid and ask are returned. This is equivalent to a book depth of 1 level. If you would like to see a larger order book, specify the level query parameter. +--------------------------------------------------------------------+ | Level | Description | +=======+============================================================+ | 1 | Only the best bid and ask | +-------+------------------------------------------------------------+ | 2 | Top 50 bids and asks (aggregated) | +-------+------------------------------------------------------------+ | 3 | Full order book (non aggregated) | +-------+------------------------------------------------------------+ If a level is not aggregated, then all of the orders at each price will be returned. Aggregated levels return only one size for each active price (as if there was only a single order for that size at the level). Levels 1 and 2 are aggregated. The first field is the price. The second is the size which is the sum of the size of the orders at that price, and the third is the number of orders, the count of orders at that price. The size should not be multiplied by the number of orders. Level 3 is non-aggregated and returns the entire order book. .. note:: This request is NOT paginated. The entire book is returned in one response. Level 1 and Level 2 are recommended for polling. For the most up-to-date data, consider using the websocket stream. .. warning:: Level 3 is only recommended for users wishing to maintain a full real-time order book using the websocket stream. Abuse of Level 3 via polling will cause your access to be limited or blocked. :param str product_id: The product id of the order book. :param int level: (optional) The level customizes the amount of detail shown. See bove for more detail. The default is 1. :returns: A dict representing the order book for the product id specified. The layout of the dict will vary based on the level. See the examples below. Level 1:: { 'sequence': 7068939079, 'bids': [['482.98', '54.49144003', 18]], 'asks': [['482.99', '4.57036219', 10]] } Level 2:: { 'sequence': 7069016926, 'bids': [['489.13', '0.001', 1], ['487.99', '0.03', 1], ...], 'asks': [['489.14', '40.72125158', 16], ['490.11', '0.5', 1], ...], } Level 3:: { 'sequence': 7072737439, 'bids': [ ['468.9', '0.01100413', '48c3ed25-616d-430d-bab4-cb338b489a33'], ['468.9', '0.224', 'b96424ea-e992-4df5-b503-df50dac1ac50'], ... ], 'asks': [ ['468.91', '5.96606527', 'cc37e457-020c-4843-9a3e-e6164dcf4e60'], ['468.91', '0.00341509', '43e8158a-30c6-437b-9a51-9b9da00e4e22'], ... ] } :raises ValueError: level not 1, 2, or 3. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if level not in (1, 2, 3): raise ValueError("level must be 1, 2, or 3") headers, body = await self.get('/products/{}/book'.format(product_id), params={'level': level}) return body
[docs] async def ticker(self, product_id): """Get information about the last trade for a specific product. .. note:: Polling is discouraged in favor of connecting via the websocket stream and listening for match messages. :param str product_id: The product id of the tick to be retrieved. :returns: A dict containing information about the last trade (tick) for the product. Example:: { 'trade_id': 51554088, 'price': '6503.14000000', 'size': '0.00532605', 'bid': '6503.13', 'ask': '6503.14', 'volume': '6060.89272148', 'time': '2018-09-27T13:18:42.571000Z' } :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ header, body = await self.get('/products/{}/ticker'.format(product_id)) return body
[docs] async def trades(self, product_id, limit=100, before=None, after=None): """List the latest trades for a product. The trade side indicates the maker order side. The maker order is the order that was open on the order book. buy side indicates a down-tick because the maker was a buy order and their order was removed. Conversely, sell side indicates an up-tick. .. admonition:: Pagination This method is paginated. See pagination_ for more details. :param str product_id: The product id whose trades are to be retrieved. :param int limit: (optional) The number of results to be returned per request. The default (and maximum) value is 100. :param int before: (optional) The before cursor value. The default is None. :param int after: (optional) The after cursor value. The default is None. :returns: A 3-tuple: (trades, before cursor, after cursor) trades is a list of dicts representing trades for the product specified. Example:: ( [ { 'time': '2018-09-27T22:49:16.105Z', 'trade_id': 51584925, 'price': '6681.01000000', 'size': '0.02350019', 'side': 'sell' }, { 'time': '2018-09-27T22:49:12.39Z', 'trade_id': 51584924, 'price': '6681.00000000', 'size': '0.01020000', 'side': 'buy' }, ... ], '51590012', '51590010' ) :raises ValueError: before and after paramters are both provided. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if before and after: raise ValueError("before and after cannot both be provided.") params = {'limit': limit} if before: params.update({'before': before}) if after: params.update({'after': after}) headers, body = await self.get('/products/{}/trades'.format(product_id), params) return (body, headers.get('cb-before', None), headers.get('cb-after', None))
[docs] async def historic_rates(self, product_id, granularity=3600, start=None, end=None): """Get historic rates for a product. Rates are returned in grouped buckets based on the requested granularity. .. note:: The maximum number of data points for a single request is 300 candles. If your selection of start/end time and granularity will result in more than 300 data points, your request will be rejected. If you wish to retrieve fine granularity data over a larger time range, you will need to make multiple requests with new start/end ranges. Historical rate data may be incomplete. No data is published for intervals where there are no ticks. .. warning:: Historical rates should not be polled frequently. If you need real-time information, use the trade and book endpoints along with the websocket feed. :param str product_id: The product id. :param int granularity: (optional) Desired timeslice in seconds. The granularity field must be one of the following values: {60, 300, 900, 3600, 21600, 86400}. Otherwise, your request will be rejected. These values correspond to timeslices representing one minute, five minutes, fifteen minutes, one hour, six hours, and one day, respectively. The default is 3600 (1 hour). :param str start: (optional) The start time as a str in ISO 8601 format. This field is optional. If it is set, then end must be set as well. If neither start nor end are set, start will default to the time relative to now() that would return 300 results based on the granularity. :param str end: (optional) The end time as a str in ISO 8601 format. This field is optional. If it is set then start must be set as well. If it is not set, end will default to now(). :returns: A list of lists where each list item is a "bucket" representing a timeslice of length granularity. The fields of the bucket are: [ time, low, high, open, close, volume ] * **time** - bucket start time as a Unix timestamp * **low** - lowest price during the bucket interval * **high** - highest price during the bucket interval * **open** - opening price (first trade) in the bucket interval * **close** - closing price (last trade) in the bucket interval * **volume** - volume of trading activity during the bucket interval Example:: [ [1538179200, 61.12, 61.75, 61.74, 61.18, 2290.8172972700004], [1538175600, 61.62, 61.8, 61.65, 61.75, 2282.2335001199995], [1538172000, 61.52, 61.79, 61.66, 61.65, 3877.4680861400007], ... ] :raises ValueError: * granularity is not one of the possible values. * Either start or end is set but not both :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if granularity not in (60, 300, 900, 3600, 21600, 86400): raise ValueError("invalid granularity {}".format(granularity)) if start and not end: raise ValueError("end must be set if start is.") if end and not start: raise ValueError("start must be set if end is.") params = {'granularity': granularity} if start and end: params.update({'start': start, 'end': end}) headers, body = await self.get('/products/{}/candles'.format(product_id), params=params) return body
[docs] async def get_24hour_stats(self, product_id): """Get 24 hr stats for a product. :param str product_id: The product id. :returns: A dict of stats for the product including: open, high, low, volume, last price, and 30 day volume. Example:: { 'open': '6710.37000000', 'high': '6786.73000000', 'low': '6452.02000000', 'volume': '9627.98224214', 'last': '6484.03000000', 'volume_30day': '238376.24964395' } :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/products/{}/stats'.format(product_id)) return body
[docs] async def currencies(self): """List known currencies. Currency codes will conform to the ISO 4217 standard where possible. Currencies which have or had no representation in ISO 4217 may use a custom code. .. note:: Not all currencies may be currently in use for trading. :returns: A list of dicts where each dict contains information about a currency. Example:: [ { "id": "BTC", "name": "Bitcoin", "min_size": "0.00000001" }, { "id": "USD", "name": "United States Dollar", "min_size": "0.01000000" }, ..., ] :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/currencies') return body
[docs] async def server_time(self): """Get the API server time. :returns: A dict with two fields: iso and epoch. iso is an ISO 8601 str, and epoch is a float. Both represent the current time at the API server. Example:: { 'iso': '2018-09-29T03:02:27.753Z', 'epoch': 1538190147.753 } :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/time') return body
[docs] async def accounts(self): """Get a list of Coinbase Pro trading accounts. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. :returns: A list of dicts where each dict contains information about a trading a account. Example:: [ { 'id': 'a764610f-334e-4ece-b4dd-f31111ed58e7', 'currency': 'USD', 'balance': '1000.0000005931528000', 'available': '1000.0000005931528', 'hold': '0.0000000000000000', 'profile_id': '019be148-d490-45f9-9ead-0d1f64127716' }, ... ] :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/accounts', auth=True) return body
[docs] async def account(self, account_id): """Retrieve information for a single account. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. :param str account_id: The account id. :returns: A dict of account information. Example:: { 'id': 'a764610f-334e-4ece-b4dd-f31111ed58e7', 'currency': 'USD', 'balance': '1000.0000005931528000', 'available': '1000.0000005931528', 'hold': '0.0000000000000000', 'profile_id': '019be148-d490-45f9-9ead-0d1f64127716' } :raises ValueError: If the client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/accounts/{}'.format(account_id), auth=True) return body
[docs] async def account_history(self, account_id, limit=100, before=None, after=None): """Retrieve a list account activity. Account activity includes transactions that either increase or decrease the account balance. Transactions are sorted latest first. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. .. admonition:: Pagination This method is paginated. See pagination_ for more details. :param str account_id: The account id. :param int limit: (optional) The number of results to be returned per request. The default (and maximum) value is 100. :param int before: (optional) The before cursor value. The default is None. :param int after: (optional) The after cursor value. The default is None. :returns: A 3-tuple (results, before cursor, after cursor) results is a list of dicts each representing an instance of account activity. The different types of activity returned are: * **transfer** Funds moved to/from Coinbase to Coinbase Pro * **match** Funds moved as a result of a trade * **fee** Fee as a result of a trade * **rebate** Fee rebate The details field contains type-specific details about the specific transaction. Example:: ( [ { 'created_at': '2018-09-28T19:31:21.211159Z', 'id': 10712040275, 'amount': '-600.9103845810000000', 'balance': '0.0000005931528000', 'type': 'match', 'details': { 'order_id': 'd2fadbb5-8769-4b80-91da-be3d9c6bd38d', 'trade_id': '34209042', 'product_id': 'BTC-USD' } }, { 'created_at': '2018-09-23T23:13:45.771507Z', 'id': 1065316993, 'amount': '-170.0000000000000000', 'balance': '6.7138918107528000', 'type': 'transfer', 'details': { 'transfer_id': 'd00841ff-c572-4726-b9bf-17e783159256', 'transfer_type': 'withdraw' } }, ... ], '1071064024', '1008063508' ) :raises ValueError: * The client is not configured for authorization. * before and after are both set. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if before and after: raise ValueError("before and after cannot both be provided.") params = {'limit': limit} if before: params.update({'before': before}) if after: params.update({'after': after}) headers, body = await self.get('/accounts/{}/ledger'.format(account_id), params=params, auth=True) return (body, headers.get('cb-before', None), headers.get('cb-after', None))
[docs] async def holds(self, account_id, limit=100, before=None, after=None): """Get any existing holds on an account. Holds are placed on an account for any active orders or pending withdraw requests. As an order is filled, the hold amount is updated. If an order is canceled, any remaining hold is removed. For a withdraw, once it is completed, the hold is removed. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. .. admonition:: Pagination This method is paginated. See pagination_ for more details. :param str account_id: The acount ID to be checked for holds. :param int limit: (optional) The number of results to be returned per request. The default (and maximum) value is 100. :param int before: (optional) The before cursor value. The default is None. :param int after: (optional) The after cursor value. The default is :returns: A 3-tuple (holds, before cursor, after cursor) holds is a list of dicts where each dict represents a hold on the account. Example:: ( [ { "id": "82dcd140-c3c7-4507-8de4-2c529cd1a28f", "account_id": "e0b3f39a-183d-453e-b754-0c13e5bab0b3", "created_at": "2014-11-06T10:34:47.123456Z", "updated_at": "2014-11-06T10:40:47.123456Z", "amount": "4.23", "type": "order", "ref": "0a205de4-dd35-4370-a285-fe8fc375a273", }, ... ], '1071064024', '1008063508' ) :raises ValueError: * The client is not configured for authorization. * before and after are both set. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if before and after: raise ValueError("before and after cannot both be provided.") params = {'limit': limit} if before: params.update({'before': before}) if after: params['after'] = after headers, body = await self.get('/accounts/{}/holds'.format(account_id), params=params, auth=True) return (body, headers.get('cb-before', None), headers.get('cb-after', None))
[docs] async def limit_order(self, side, product_id, price, size, time_in_force='GTC', cancel_after=None, post_only=False, client_oid=None, stp='dc', stop=None, stop_price=None): """Place a limit order or a stop entry/loss limit order. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "trade" permission. :param str side: Either buy or sell :param str product_id: The product id to be bought or sold. :param float price: The price the order is to be executed at. This paramater may also be a string to avoid floating point issues. :param float size: The quantity of the cryptocurrency to buy or sell. This parameter may also be a string. :param str time_in_force: (optional) Time in force policies provide guarantees about the lifetime of an order. There are four policies: GTC (good till canceled), GTT (good till time), IOC (immediate or cancel), and FOK (fill or kill) GTT requires that cancel_after be set. IOC and FOK require post_only be False. The default is GTC. :param str cancel_after: (optional) The length of time before a GTT order is cancelled. Must be either min, hour, or day. time_in_force must be GTT or an error is raised. If cancel_after is not set for a GTT order, the order will be treated as GTC. The default is None. :param bool post_only: (optional) Indicates that the order should only make liquidity. If any part of the order results in taking liquidity, the order will be rejected and no part of it will execute. This flag is ignored for IOC and FOK orders. This value must be False for all stop orders. The default is False. :param str stp: (optional) Self trade preservation flag. The possible values are dc (decrease and cancel), co (cancel oldest), cn (cancel newest), or cb (cancel both). The default is dc. :param str stop: (optional) If this is a stop order, this value must be either loss or entry. Requires stop_price to be set. The default is None. :param float stop_price: (optioinal) The trigger price for stop orders. Required if stop is set. This may also be a string. The default is None. .. warning:: As of 11/18, sending anything other than dc for stp while testing in Coinbase Pro's sandbox yields an APIRequestError "Invalid stp..." even though the Coinbase API documentation claims the other options for stp are valid. Change this from dc at your own risk. .. note:: To see a more detailed explanation of these parameters and to learn more about the order life cycle, please see the official Coinbase Pro API documentation at: https://docs.gdax.com/#channels. :returns: A dict of information about the order. Example:: { 'id': '97059421-3033-4cf4-99cb-925c1bf2c54f', 'price': '35.00000000', 'size': '0.00100000', 'product_id': 'BTC-USD', 'side': 'buy', 'stp': 'dc', 'type': 'limit', 'time_in_force': 'GTC', 'post_only': False, 'created_at': '2018-11-25T21:24:37.166378Z', 'fill_fees': '0.0000000000000000', 'filled_size': '0.00000000', 'executed_value': '0.0000000000000000', 'status': 'pending', 'settled': False } :raises ValueError: * The client is not configured for authorization. * The side is not either "buy" or "sell". * The time_in_force is not GTC, GTT, IOC or FOK. * time_in_force is GTT but cancel_after is not set * cancel_after for a limit order is set but isn't min, hour or day. * cancel_after is set for a limit order but time_in_force isn't GTT. * The time_in_force is IOC or FOK and post_only is True * stp is a value other than dc. co, cn, or cb. * stop is set to something other than loss or entry. * A stop order does not have stop_price set. * stop_price is set but stop is not * A stop_order has post_only set to True :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if side not in ('buy', 'sell'): raise ValueError("Invalid side: {}. Must be either buy or sell".format(side)) if time_in_force not in ('GTC', 'GTT', 'IOC', 'FOK'): raise ValueError('time_in_force must be GTC, GCC, IOC or FOK.') if time_in_force == 'GTT' and not cancel_after: raise ValueError('cancel_after required for GTT time_in_force.') if cancel_after and cancel_after not in ('min', 'hour', 'day'): raise ValueError('cancel_after must be min, hour, or day.') if cancel_after and not time_in_force == 'GTT': raise ValueError('cancel_after requires time_in_force to be GTT.') if (time_in_force == 'IOC' or time_in_force == 'FOK') and post_only: raise ValueError( 'post_only must be False for time_in_force {}'.format(time_in_force)) if stp not in ('dc', 'co', 'cn', 'cb'): raise ValueError('Invalid stp: {}. Must be dc, co, cn, or cb.'.format(stp)) if stop and stop not in ('loss', 'entry'): raise ValueError("Invalid stop: {}. Must be either loss or entry.".format(stop)) if stop and not stop_price: raise ValueError("Stop orders must have stop_price set.") if stop_price and not stop: raise ValueError("Stop orders must have the stop parameter set.") if stop and post_only: raise ValueError("post_only must be False for stop orders.") data = {'type': 'limit', 'side': side, 'product_id': product_id, 'price': price, 'size': size, 'time_in_force': time_in_force, 'post_only': post_only, 'stp': stp} if cancel_after: data['cancel_after'] = cancel_after if client_oid: data['client_oid'] = client_oid if stop: data['stop'] = stop data['stop_price'] = stop_price headers, body = await self.post('/orders', data=data, auth=True) return body
[docs] async def market_order(self, side, product_id, size=None, funds=None, client_oid=None, stp='dc', stop=None, stop_price=None): """Place a market order or a stop entry/loss market order. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "trade" permission. :param str side: Either buy or sell :param str product_id: The product id to be bought or sold. :param float size: The quantity of the cryptocurrency to buy or sell. Either size or funds must be set for a market order but not both. This may also be a string. The default is None. :param float funds: This is the amount of quote currency to be used for a purchase (buy) or the amount to be obtained from a sale (sell). Either size or funds must be set for a market order but not both. This may also be a string. The default is None. :param str client_oid: (optional) A self generated UUID to identify the order. The default is None. :param str stp: (optional) Self trade preservation flag. The possible values are dc (decrease and cancel), co (cancel oldest), cn (cancel newest), or cb (cancel both). The default is dc. :param str stop: (optional) If this is a stop order, this value must be either loss or entry. Requires stop_price to be set. The default is None. :param float stop_price: (optional) The trigger price for stop orders. Required if stop is set. This may also be a string. The default is None. .. warning:: As of 11/18, sending anything other than dc for stp while testing in Coinbase Pro's sandbox yields an APIRequestError "Invalid stp..." even though the Coinbase API documentation claims the other options for stp are valid. Change this from dc at your own risk. .. note:: To see a more detailed explanation of these parameters and to learn more about the order life cycle, please see the official Coinbase Pro API documentation at: https://docs.gdax.com/#channels. :returns: A dict of information about the order. Example:: { 'id': '37e81782-cc45-4ecc-a9ff-59c327bb1d40', 'size': '0.00100000', 'product_id': 'BTC-USD', 'side': 'sell', 'stp': 'dc', 'type': 'market', 'post_only': False, 'created_at': '2018-11-25T21:28:04.788042Z', 'fill_fees': '0.0000000000000000', 'filled_size': '0.00000000', 'executed_value': '0.0000000000000000', 'status': 'pending', 'settled': False } :raises ValueError: * The client is not configured for authorization. * The side is not either "buy" or "sell". * Neither size nor funds is set. * Both size and funds are set * stp is a value other than dc. co, cn, or cb. * stop is set to something other than loss or entry. * A stop order does not have stop_price set. * stop_price is set but stop is not :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if side not in ('buy', 'sell'): raise ValueError("Invalid side: {}. Must be either buy or sell".format(side)) if not (size or funds): raise ValueError('Market orders must have size or funds set.') if size and funds: raise ValueError("Market orders can't have both size and funds set.") if stp not in ('dc', 'co', 'cn', 'cb'): raise ValueError('Invalid stp: {}. Must be dc, co, cn, or cb.'.format(stp)) if stop and stop not in ('loss', 'entry'): raise ValueError("Invalid stop: {}. Must be either loss or entry.".format(stop)) if stop and not stop_price: raise ValueError("Stop orders must have stop_price set.") if stop_price and not stop: raise ValueError("Stop orders must have the stop parameter set.") data = {'type': 'market', 'side': side, 'product_id': product_id, 'stp':stp} if size: data['size'] = size if funds: data['funds'] = funds if client_oid: data['client_oid'] = client_oid if stop: data['stop'] = stop data['stop_price'] = stop_price headers, body = await self.post('/orders', data=data, auth=True) return body
[docs] async def cancel(self, order_id): """Cancel a previously placed order. If the order had no matches during its lifetime its record may be purged. This means the order details will not be available with :meth:`copra.rest.Client.get_order`. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "trade" permission. :param str order_id: The id of the order to be cancelled. This is the server-assigned order id and not the optional client_oid. :returns: A list consisting of a single string entry, the id of the cancelled order. Example:: ["144c6f8e-713f-4682-8435-5280fbe8b2b4"] :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.delete('/orders/{}'.format(order_id), auth=True) return body
[docs] async def cancel_all(self, product_id=None, stop=False): """Cancel "all" orders. The default behavior of this method (and the underlying Coinbase API method) is to cancel only *open* orders. Stop orders, once placed, have a status method of *active* and will not be cancelled. Setting **stop** = True will also cancel stop orders or :meth:`copra.rest.Client.cancel` can be used to cancel invidual stop orders. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "trade" permission. :param str product_id: (optional) Only cancel orders for the specified product. The default is None. :param bool stop: (optional) Also delete stop orders. :returns: A list of the ids of orders that were successfully cancelled. Example:: [ "144c6f8e-713f-4682-8435-5lia280fbe8b2b4", "debe4907-95dc-442f-af3b-cec12f42ebda", "cf7aceee-7b08-4227-a76c-3858144323ab", "dfc5ae27-cadb-4c0c-beef-8994936fde8a", "34fecfbf-de33-4273-b2c6-baf8e8948be4" ] :raises ValueError: The client is not configured for authorization. :raises APIRequestError: For any error generated by the Coinbase Pro API server. """ params = {'product_id': product_id} if product_id else {} headers, cancelled = await self.delete('/orders', params=params, auth=True) if stop: orders, _, _ = await self.orders(['active', 'open', 'pending']) for order in orders: try: if not product_id or product_id == order['product_id']: resp = await self.cancel(order['id']) if len(resp): cancelled.append(resp[0]) except APIRequestError: pass return cancelled
[docs] async def orders(self, status=None, product_id=None, limit=100, before=None, after=None): """Retrieve a list orders. The status of an order may change between the request and response depending on market conditions. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. .. admonition:: Pagination This method is paginated. See pagination_ for more details. :param str status: (optional) Limit list of orders to one or more of these statuses: open, pending, active or all. status may be a single string or a list of strings. i.e, ['open', 'active']. 'all' returns orders of all statuses. The default is ['open', 'active', 'pending']. :param str product_id: (optional) Filter orders by product_id :param int limit: (optional) The number of results to be returned per request. The default (and maximum) value is 100. :param int before: (optional) The before cursor value. The default is None. :param int after: (optional) The after cursor value. The default is None. :returns: A 3-tuple: (orders, before cursor, after cursor) orders is a list of dicts where each dict represents an order. Example:: ([ { "id": "d0c5340b-6d6c-49d9-b567-48c4bfca13d2", "price": "0.10000000", "size": "0.01000000", "product_id": "BTC-USD", "side": "buy", "stp": "dc", "type": "limit", "time_in_force": "GTC", "post_only": false, "created_at": "2016-12-08T20:02:28.53864Z", "fill_fees": "0.0000000000000000", "filled_size": "0.00000000", "executed_value": "0.0000000000000000", "status": "open", "settled": false }, { "id": "8b99b139-58f2-4ab2-8e7a-c11c846e3022", "price": "1.00000000", "size": "1.00000000", "product_id": "BTC-USD", "side": "buy", "stp": "dc", "type": "limit", "time_in_force": "GTC", "post_only": false, "created_at": "2016-12-08T20:01:19.038644Z", "fill_fees": "0.0000000000000000", "filled_size": "0.00000000", "executed_value": "0.0000000000000000", "status": "open", "settled": false } ], '1071064024', '1008063508' ) :raises ValueError: * The client is not configured for authorization. * An invalid status string is provided. * before and after are both set. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if before and after: raise ValueError("before and after cannot both be provided.") params = CIMultiDict({'limit': limit}) if before: params.update({'before': before}) if after: params.update({'after': after}) if status: if isinstance(status, str): status = [status] for value in status: if value not in ('active', 'all', 'open', 'pending', 'done'): raise ValueError("Invalid status: {}".format(value)) params.update([('status', value) for value in status]) if product_id: params['product_id'] = product_id headers, body = await self.get('/orders', params=params, auth=True) return (body, headers.get('cb-before', None), headers.get('cb-after', None))
[docs] async def get_order(self, order_id): """Get a single order by order id. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. :param str order_id: The order id. :returns: A dict of information about the order. Example:: { "id": "68e6a28f-ae28-4788-8d4f-5ab4e5e5ae08", "size": "1.00000000", "product_id": "BTC-USD", "side": "buy", "stp": "dc", "funds": "9.9750623400000000", "specified_funds": "10.0000000000000000", "type": "market", "post_only": false, "created_at": "2016-12-08T20:09:05.508883Z", "done_at": "2016-12-08T20:09:05.527Z", "done_reason": "filled", "fill_fees": "0.0249376391550000", "filled_size": "0.01291771", "executed_value": "9.9750556620000000", "status": "done", "settled": true } .. note:: Open orders may change state between the request and the response depending on market conditions. :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/orders/{}'.format(order_id), auth=True) return body
[docs] async def fills(self, order_id='', product_id='', limit=100, before=None, after=None): """Get a list of recent fills. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. .. admonition:: Pagination This method is paginated. See pagination_ for more details. :param str order_id: (optional) Limit list of fills to this order_id, Either this or product_id must be defined. :param str product_id: (optional) Limit list of fills to this product_id. Either this or order_id must be defined. :param int limit: (optional) The number of results to be returned per request. The default (and maximum) value is 100. :param int before: (optional) The before cursor value. The default is None. :param int after: (optional) The after cursor value. The default is None. :returns: A 3-tuple (fills, before cursor, after cursor) fills is a list of dicts where each dict represents a fill. Example:: [ { "trade_id": 74, "product_id": "BTC-USD", "price": "10.00", "size": "0.01", "order_id": "d50ec984-77a8-460a-b958-66f114b0de9b", "created_at": "2014-11-07T22:19:28.578544Z", "liquidity": "T", "fee": "0.00025", "settled": true, "side": "buy" }, ..., ] :raises ValueError: * The client is not configured for authorization. * Both before and after are set. * Neither order_id nor product_id are set or both are set. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if before and after: raise ValueError("before and after cannot both be provided.") if not order_id and not product_id: raise ValueError("Either order_id or product_id must be defined.") if order_id and product_id: raise ValueError("order_id or product_id cannot both be sent.") params = CIMultiDict({'limit': limit}) if before: params['before'] = before if after: params['after'] = after if order_id: params['order_id'] = order_id if product_id: params['product_id'] = product_id headers, body = await self.get('/fills', params=params, auth=True) return (body, headers.get('cb-before', None), headers.get('cb-after', None))
[docs] async def payment_methods(self): """Get a list of the payment methods you have on file. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "transfer" permission. :returns: A list of dicts where each dict contains detailed information about a payment method the account has available. Example:: [ { "id": "bc6d7162-d984-5ffa-963c-a493b1c1370b", "type": "ach_bank_account", "name": "Bank of America - eBan... ********7134", "currency": "USD", "primary_buy": true, "primary_sell": true, "allow_buy": true, "allow_sell": true, "allow_deposit": true, "allow_withdraw": true, "limits": { "buy": [ { "period_in_days": 1, "total": { "amount": "10000.00", "currency": "USD" }, "remaining": { "amount": "10000.00", "currency": "USD" } } ], "instant_buy": [ { "period_in_days": 7, "total": { "amount": "0.00", "currency": "USD" }, "remaining": { "amount": "0.00", "currency": "USD" } } ], "sell": [ { "period_in_days": 1, "total": { "amount": "10000.00", "currency": "USD" }, "remaining": { "amount": "10000.00", "currency": "USD" } } ], "deposit": [ { "period_in_days": 1, "total": { "amount": "10000.00", "currency": "USD" }, "remaining": { "amount": "10000.00", "currency": "USD" } } ] } }, ] :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/payment-methods', auth=True) return body
[docs] async def coinbase_accounts(self): """Get a list of your coinbase accounts. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "transfer" permission. :returns: A list of dicts where each dict contains information about a Coinbase account. Example:: [ { "id": "fc3a8a57-7142-542d-8436-95a3d82e1622", "name": "ETH Wallet", "balance": "0.00000000", "currency": "ETH", "type": "wallet", "primary": false, "active": true }, { "id": "2ae3354e-f1c3-5771-8a37-6228e9d239db", "name": "USD Wallet", "balance": "0.00", "currency": "USD", "type": "fiat", "primary": false, "active": true, "wire_deposit_information": { "account_number": "0199003122", "routing_number": "026013356", "bank_name": "Metropolitan Commercial Bank", "bank_address": "99 Park Ave 4th Fl New York, NY 10016", "bank_country": { "code": "US", "name": "United States" }, "account_name": "Coinbase, Inc", "account_address": "548 Market Street, San Fran, CA 94104", "reference": "BAOCAEUX" } }, { "id": "1bfad868-5223-5d3c-8a22-b5ed371e55cb", "name": "BTC Wallet", "balance": "0.00000000", "currency": "BTC", "type": "wallet", "primary": true, "active": true }, ..., ] :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/coinbase-accounts', auth=True) return body
[docs] async def deposit_payment_method(self, amount, currency, payment_method_id): """Deposit funds from a payment method on file. To get a list of available payment methods, use :meth:`copra.rest.Client.payment_methods`. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "transfer" permission. :param float amount: The amount of the currency to deposit. This paramater may also be a string to avoid floating point issues. :param str currency: The type of currency to deposit. i.e., USD, EUR, etc. :param str payment_method_id: The id of the payment method to use. :returns: A dict with a deposit id, timestamp and other deposit information. Example:: { "id": "593533d2-ff31-46e0-b22e-ca754147a96a", "amount": "10.00", "currency": "USD", "payout_at": "2016-08-20T00:31:09Z" } :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.post('/deposits/payment-method', data={'amount': amount, 'currency': currency, 'payment_method_id': payment_method_id}, auth=True) return body
[docs] async def deposit_coinbase(self, amount, currency, coinbase_account_id): """Deposit funds from a Coinbase account. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "transfer" permission. :param float amount: The amount of the currency to deposit. This paramater may also be a string to avoid floating point issues. :param str currency: The type of currency to deposit. i.e., BTC, LTC, USD, etc. :param str coinbase_account_id: The id of the Coinbase account to deposit from. To get a list of Coinbase accounts, use: :meth:`copra.rest.Client.coinbase_accounts`. :returns: A dict with a deposit id and confirmation of the deposit amount and currency. Example:: { "id": "593533d2-ff31-46e0-b22e-ca754147a96a", "amount": "10.00", "currency": "BTC", } :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.post('/deposits/coinbase-account', data={'amount': amount, 'currency': currency, 'coinbase_account_id': coinbase_account_id}, auth=True) return body
[docs] async def withdraw_payment_method(self, amount, currency, payment_method_id): """Withdraw funds to a payment method on file. To get a list of available payment methods, use :meth:`copra.rest.Client.payment_methods`. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "transfer" permission. :param float amount: The amount of the currency to withdraw. This paramater may also be a string to avoid floating point issues. :param str currency: The type of currency to withdraw. i.e., USD, EUR, etc. :param str payment_method_id: The id of the payment method on file to use. :returns: A dict with a withdrawal id, timestamp, and other withdrawal information. Example:: { "id":"593533d2-ff31-46e0-b22e-ca754147a96a", "amount": "10.00", "currency": "USD", "payout_at": "2016-08-20T00:31:09Z" } :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.post('/withdrawals/payment-method', data={'amount': amount, 'currency': currency, 'payment_method_id': payment_method_id}, auth=True) return body
[docs] async def withdraw_coinbase(self, amount, currency, coinbase_account_id): """Withdraw funds to a coinbase account. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "transfer" permission. :param float amount: The amount of the currency to withdraw. This paramater may also be a string to avoid floating point issues. :param str currency: The type of currency to withdraw. i.e., BTC, LTC, USD, etc. :param str coinbase_account_id: The id of the Coinbase account to withdraw to. To get a list of Coinbase accounts, use: :meth:`copra.rest.Client.coinbase_accounts`. :returns: A dict with the withdrawal id, and confirmation of the withdrawl amount and currency. Example:: { "id":"593533d2-ff31-46e0-b22e-ca754147a96a", "amount":"10.00", "currency": "BTC", } :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.post('/withdrawals/coinbase-account', data={'amount': amount, 'currency': currency, 'coinbase_account_id': coinbase_account_id}, auth=True) return body
[docs] async def withdraw_crypto(self, amount, currency, crypto_address): """Withdraw funds to a crypto address. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "transfer" permission. :param float amount: The amount of the currency to withdraw. This paramater may also be a string to avoid floating point issues. :param str currency: The type of currency to withdraw. i.e., BTC, LTC, USD, etc. :param str crypto_address: The crypto address of the recipient. :returns: A dict with the withrawal id and confirmation of the withdrawl amount and currency. Example:: { "id":"593533d2-ff31-46e0-b22e-ca754147a96a", "amount":"10.00", "currency": "BTC", } :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.post('/withdrawals/crypto', data={'amount': amount, 'currency': currency, 'crypto_address': crypto_address}, auth=True) return body
[docs] async def stablecoin_conversion(self, from_currency_id, to_currency_id, amount): """Convert to and from a stablecoin. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have the "trade" permission. .. note:: As of November, 1018, Coinbase Pro only supports USD-USDC conversions :param str from_currency_id: The id of the currency to convert from. :param str to_currency_id: The id of the currency to convert to. :param float amount: The amount of currency to convert. This paramater may also be a string to avoid floating point issues. :returns: A dict summarizing the conversion. Example:: { "id": "8942caee-f9d5-4600-a894-4811268545db", "amount": "10000.00", "from_account_id": "7849cc79-8b01-4793-9345-bc6b5f08acce", "to_account_id": "105c3e58-0898-4106-8283-dc5781cda07b", "from": "USD", "to": "USDC" } :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.post('/conversions', data={'from': from_currency_id, 'to': to_currency_id, 'amount': amount}, auth=True) return body
[docs] async def fees(self): """Get your current maker & taker fee rates and 30-day trailing volume. .. note:: Quoted rates are subject to change. More information on fees can found at: https://support.pro.coinbase.com/customer/en/portal/articles/2945310-fees. :returns: A dict containing the maker fee, taker fee and 30-day trailing volume. Example:: { "maker_fee_rate": "0.0015", "taker_fee_rate": "0.0025", "usd_volume": "25000.00" } :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/fees', auth=True) return body
[docs] async def create_report(self, report_type, start_date, end_date, product_id='', account_id='', report_format='pdf', email=''): """Create a report about your account history. Reports provide batches of historic information about your account in various human and machine readable forms. The report will be generated when resources are available. Report status can be queried via :meth:`copra.rest.Client.report_status`. The url for the report file will be available once the report has successfully been created and is available for download. .. note:: Reports are only available for download for a few days after being created. Once a report expires, the report is no longer available for download and is deleted. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. :param str report_type: The type of report to generate. This must be either "fills" or "account". :param str start_date: The starting date of the requested report as a str in ISO 8601 format. :param str end_date: The ending date of the requested report as a str in ISO 8601 format. :param str product_id: (optional) ID of the product to generate a fills report for. e.g. "BTC-USD". Required if type is fills. :param str account_id: (optional) ID of the account to generate an account report for. Required if type is account. :param str report_format: (optional) Format of the report to be generated. Can be either pdf or csv. The default is pdf. :param str email: (optional) Email address to send the report to. The default is None. :returns: A dict of information about the report including its id which can be used to check its status. Example:: { "id": "0428b97b-bec1-429e-a94c-59232926778d", "type": "fills", "status": "pending", "created_at": "2015-01-06T10:34:47.000Z", "completed_at": undefined, "expires_at": "2015-01-13T10:35:47.000Z", "file_url": undefined, "params": { "start_date": "2014-11-01T00:00:00.000Z", "end_date": "2014-11-30T23:59:59.000Z" } } :raises ValueError: * The client is not configured for authorization. * Invalid report_type provided. * report_type is fills and product_id is not provided. * report_type is account and account_id is not provided. * Invalid report_format provided. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ if report_type not in ("account", "fills"): raise ValueError( "Invalid report_type: {}. Must be 'fills' or 'account'.".format( report_type)) if report_type == 'fills' and not product_id: raise ValueError("product_id must be defined for report_type fills.") if report_type == 'account' and not account_id: raise ValueError("account_id must be defined for report_type account.") if report_format not in ('csv', 'pdf'): raise ValueError( "Invalid format {}. Must be either 'csv' or 'pdf'.".format(report_format)) data = { 'type': report_type, 'start_date': start_date, 'end_date': end_date, 'format': report_format } if product_id: data['product_id'] = product_id if account_id: data['account_id'] = account_id if email: data['email'] = email headers, body = await self.post('/reports', data=data, auth=True) return body
[docs] async def report_status(self, report_id): """Get the status of a report. Once a report request has been accepted for processing, the status is available by polling the report resource endpoint. The possible status values are: * **pending** - The report request has been accepted and is awaiting processing. * **creating** - The report is being created. * **ready** - The report is ready for download from file_url. The final report will be uploaded and available at file_url once the status indicates ready. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. :param str report_id: The id of the report. This is obtained from :meth:`copra.rest.Client.create_report`. :returns: A dict summarizing the current status of the report. Examples follow. Creating report:: { "id": "0428b97b-bec1-429e-a94c-59232926778d", "type": "fills", "status": "creating", "created_at": "2015-01-06T10:34:47.000Z", "completed_at": undefined, "expires_at": "2015-01-13T10:35:47.000Z", "file_url": undefined, "params": { "start_date": "2014-11-01T00:00:00.000Z", "end_date": "2014-11-30T23:59:59.000Z" } } Finished report:: { "id": "0428b97b-bec1-429e-a94c-59232926778d", "type": "fills", "status": "ready", "created_at": "2015-01-06T10:34:47.000Z", "completed_at": "2015-01-06T10:35:47.000Z", "expires_at": "2015-01-13T10:35:47.000Z", "file_url": "https://example.com/0428b97b.../fills.pdf", "params": { "start_date": "2014-11-01T00:00:00.000Z", "end_date": "2014-11-30T23:59:59.000Z" } } :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/reports/{}'.format(report_id), auth=True) return body
[docs] async def trailing_volume(self): """Return your 30-day trailing volume for all products. This is a cached value that’s calculated every day at midnight UTC. .. admonition:: Authorization :class: attention This method requires authorization. The API key must have either the "view" or "trade" permission. :returns: A list of dicts where each dict contains information about a specific product that was traded. Example:: [ { "product_id": "BTC-USD", "exchange_volume": "11800.00000000", "volume": "100.00000000", "recorded_at": "1973-11-29T00:05:01.123456Z" }, { "product_id": "LTC-USD", "exchange_volume": "51010.04100000", "volume": "2010.04100000", "recorded_at": "1973-11-29T00:05:02.123456Z" } ] :raises ValueError: The client is not configured for authorization. :raises APIRequestError: Any error generated by the Coinbase Pro API server. """ headers, body = await self.get('/users/self/trailing-volume', auth=True) return body