Metadata-Version: 2.4
Name: zerocap_api
Version: 0.1.38
Summary: Python SDK (REST and websocket clients) for the Zerocap DMA API
Home-page: https://zerocap.com/
Author: zerocap
Author-email: maomao@zerocap.com
License: MIT
Project-URL: Documentation, https://docs.zerocap.com/reference
Project-URL: Source, https://github.com/zerocap-org/zerocap_dma_admin/tree/bugfix/fb/6.0.0/tests/zerocap_api
Project-URL: Changelog, https://pypi.org/project/zerocap-api/#changelog
Platform: all
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests
Requires-Dist: websockets>=11.0.3
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: platform
Dynamic: project-url
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary


# zerocap-api

Python SDK for the [Zerocap DMA API](https://docs.zerocap.com/reference): `ZerocapRestClient` places and queries
orders, requests quotes and reads balances, transfers and wallets; `ZerocapWebsocketClient` streams prices and
order and trade updates. Requires Python 3.8 or later.

```
pip install zerocap-api
```

API keys are issued by Zerocap; contact Zerocap to register. Both clients take `env='sandbox'` (for testing) or
`env='prod'` and sign every request for you. The full API reference is at https://docs.zerocap.com/reference.

## Contents

- [REST API](#rest-api)
  1. [Create an order](#1-create-an-order)
  2. [Fetch an order](#2-fetch-an-order)
  3. [Fetch orders](#3-fetch-orders)
  4. [Request for quote](#4-request-for-quote)
  5. [Get instruments](#5-get-instruments)
  6. [Get balances](#6-get-balances)
  7. [Get transfers](#7-get-transfers)
  8. [Get time](#8-get-time)
  9. [Get risk](#9-get-risk)
  10. [Request for firm quote](#10-request-for-firm-quote)
  11. [Accept quote](#11-accept-quote)
  12. [Get external wallets](#12-get-external-wallets)
  13. [Initiate withdrawal](#13-initiate-withdrawal)
  14. [Initiate transfer](#14-initiate-transfer)
  - [Errors](#errors)
- [Websocket](#websocket)
  1. [Market data](#1-subscribe-to-market-data)
  2. [Order updates](#2-subscribe-to-order-updates)
  3. [Trades](#3-subscribe-to-trades)
- [Changelog](#changelog)

## <a id="rest-api"></a>REST API

```
import time
from zerocap_api import ZerocapRestClient

# API key and secret are required, please contact zerocap to register.
# Environments: 'prod' and 'sandbox' (any other value raises ValueError).

api_key = "***"
api_secret = "***"
client = ZerocapRestClient(api_key, api_secret, env='sandbox')

```

The client adds the authentication headers itself. The raw-request examples below show them for reference:
`api-key`, `timestamp` (integer Unix seconds) and `signature` (lowercase hex HMAC-SHA256 of the `timestamp`
string, keyed with your API secret).

### <a id="1-create-an-order"></a>1. Create an order
<!--post https://sandbox-api.zerocap.com/v2/orders/create_order-->

```

result = client.create_order(
                    symbol='USDT/AUD', 
                    side='buy', 
                    type='limit', 
                    amount='100', 
                    price='1000', 
                    )

```


Request parameters:

| Parameter              | required | data type | describe               | Value range  |
|------------------------|----------|-----------|------------------------|--------------|
| symbol                 | true     | string    | Instrument             | USDT/AUD     |
| side                   | true     | string    | Side                   | buy sell     |
| type                   | true     | string    | Type                   | limit        |
| amount                 | true     | string    | Quantity               |              |
| price                  | true     | string    | Price                  |              |
| client_order_id        | false    | string    | client_order_id        |              |


Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
headers = {
    'api-key': api_key,
    'signature': signature,
    'Content-Type': 'application/json',
    'timestamp': str(timestamp),
}

data = 
{
    "symbol": "USDT/AUD",
    "side": "buy",
    "type": "limit",
    "amount": "1000",
    "price": "1000",
}


```

Response data:

| Parameter          | required | data type | describe        | Value range |
|--------------------|----------|-----------|-----------------|-------------|
| id                 | true     | string    | Transaction ID  |             |
| timestamp          | true     | long      | Time            |             |
| last_trade_timestamp | true     | long      | Time            |             |
| status             | true     | string    | Status          |             |
| type               | true     | string    | Type            |             |
| time_in_force        | true     | string    | time_in_force     |             |
| side               | true     | string    | Side            |             |
| price              | true     | float     | Price           |             |
| average            | true     | float     | average         |             |
| amount             | true     | float     | Quantity        |             |
| filled             | true     | float     | filled          |             |
| remaining          | true     | float     | remaining       |             |
| cost               | true     | float     | cost            |             |
| fee                | true     | float     | fee             |             |
| error_message      | true     | string    | error_message   |             |
| trades             | true     | list      | trades          |             |
| client_order_id    | true     | string    | client_order_id |             |


Response example:

```

{
	"id": "d8dabfbd-e541-4262-b6c2-9f841ef220f6",
	"timestamp": 1692346145000,
	"last_trade_timestamp": 1692346145000,
	"status": "closed",
	"symbol": "USDT/AUD",
	"type": "limit",
	"time_in_force": "FOK",
	"side": "sell",
	"price": 1,
	"average": 1.48249,
	"amount": 500,
	"filled": 500,
	"remaining": 0,
	"cost": 741.245,
	"fee": 0,
	"error_message": "",
	"client_order_id": "",
	"trades": [
		{
			"id": "e86fcf94-88e6-46d8-8cc6-e06891eab6cf",
			"timestamp": 1692346145000,
			"symbol": "USDT/AUD",
			"order": "d8dabfbd-e541-4262-b6c2-9f841ef220f6",
			"type": "limit",
			"side": "sell",
			"taker_or_maker": "taker",
			"price": 1.48249,
			"amount": 500,
			"cost": 741.245,
			"fee": 0
		}
	]
}

```




### <a id="2-fetch-an-order"></a>2. Fetch an order
<!--post https://sandbox-api.zerocap.com/v2/orders/fetch_order-->
```
result = client.fetch_order(
                        id='',
                        )
```

Request parameters:


| Parameter       | required | data type | describe       | Value range  |
|-----------------|----------|-----------|----------------|--------------|
| id              | true     | string    | Transaction ID |              |

Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
headers = {
    'api-key': api_key,
    'signature': signature,
    'Content-Type': 'application/json',
    'timestamp': str(timestamp),
}

data = 
{
    "id": "d8dabfbd-e541-4262-b6c2-9f841ef220f6",
}


```

Response data:


| Parameter          | required | data type | describe        | Value range |
|--------------------|----------|-----------|-----------------|-------------|
| id                 | true     | string    | Transaction ID  |             |
| timestamp          | true     | long      | Time            |             |
| last_trade_timestamp | true     | long      | Time            |             |
| status             | true     | string    | Status          |             |
| type               | true     | string    | Type            |             |
| time_in_force        | true     | string    | time_in_force     |             |
| side               | true     | string    | Side            |             |
| price              | true     | float     | Price           |             |
| average            | true     | float     | average         |             |
| amount             | true     | float     | Quantity        |             |
| filled             | true     | float     | filled          |             |
| remaining          | true     | float     | remaining       |             |
| cost               | true     | float     | cost            |             |
| fee                | true     | float     | fee             |             |
| error_message      | true     | string    | error_message   |             |
| trades             | true     | list      | trades          |             |
| client_order_id    | true     | string    | client_order_id |             |

Response example:

```

{
	"id": "d8dabfbd-e541-4262-b6c2-9f841ef220f6",
	"timestamp": 1692346145000,
	"last_trade_timestamp": 1692346145000,
	"status": "closed",
	"symbol": "USDT/AUD",
	"type": "limit",
	"time_in_force": "FOK",
	"side": "sell",
	"price": 1,
	"average": 1.48249,
	"amount": 500,
	"filled": 500,
	"remaining": 0,
	"cost": 741.245,
	"fee": 0,
	"error_message": "",
	"trades": [
		{
			"id": "e86fcf94-88e6-46d8-8cc6-e06891eab6cf",
			"timestamp": 1692346145000,
			"symbol": "USDT/AUD",
			"order": "d8dabfbd-e541-4262-b6c2-9f841ef220f6",
			"type": "limit",
			"side": "sell",
			"taker_or_maker": "taker",
			"price": 1,
			"amount": 500,
			"cost": 741.245,
			"fee": 0
		}
	]
}

```


### <a id="3-fetch-orders"></a>3. Fetch orders
<!--post https://sandbox-api.zerocap.com/v2/orders/fetch_orders-->

```
result = client.fetch_orders(
                    symbol='USDT/AUD',
                    end_datetime=int(time.time() * 1000),
                    start_datetime=int(time.time() * 1000 - 10*86400*1000),
                    limit=10,
                    )
```



Request parameters:

| Parameter      | required  | data type | describe                                             | Value range |
|----------------|-----------|-----------|------------------------------------------------------|-------------|
| symbol         | false     | string    | symbol                                               |             |
| start_datetime | false     | int       | Start time, Unix milliseconds (inclusive)            |             |
| end_datetime   | false     | int       | End time, Unix milliseconds (exclusive)              |             |
| page           | false     | int       | Page number, starting from 1 (default 1)             |             |
| limit          | false     | int       | Orders per page (default and maximum 500)            |             |
| ids            | false     | string    | Transaction ids(null character string or id1,id2...) |             |
| status         | false     | string    | status                                               |             |
| sort_order     | false     | string    | sort_order                                           |             |
| order_type     | false     | string    | order_type                                           |             |
| side           | false     | string    | side                                                 |             |

Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
headers = {
    'api-key': api_key,
    'signature': signature,
    'Content-Type': 'application/json',
    'timestamp': str(timestamp),
}

data = 
{
    "symbol": "USDT/AUD",
    "start_datetime": 0,
    "end_datetime": 0,
    "page": 1,
    "limit": 500,
    "ids": "",
    "status": "",
    "sort_order": "",
    "order_type": "",
    "side": "",
}

```

Response data:



| Parameter            | required | data type   | describe             | Value range |
|----------------------|----------|-------------|----------------------|-------------|
| id                   | true     | string      | Transaction ID       |             |
| timestamp            | true     | long        | timestamp            |             |
| last_trade_timestamp | true     | long        | last_trade_timestamp |             |
| status               | true     | string      | status               |             |
| symbol               | true     | string      | symbol               |             |
| type                 | true     | string      | type                 |             |
| time_in_force        | true     | string      | time_in_force        |             |
| side                 | true     | string      | side                 |             |
| price                | true     | float       | price                |             |
| average              | true     | float       | average              |             |
| amount               | true     | float       | amount               |             |
| filled               | true     | float       | filled               |             |
| remaining            | true     | float       | remaining            |             |
| cost                 | true     | float       | cost                 |             |
| fee                  | true     | float       | fee                  |             |
| error_message        | true     | string      | fee                  |             |
| trades               | true     | list        | trades               |             |
| total                | true     | int         | total                |             |
| page                 | true     | int         | page                 |             |
| client_order_id      | true     | string      | client_order_id      |             |


Response example:

```

{
	"order_list": [
		{
			"id": "e1af8ae0-3247-4755-87ad-f683fed3aff5",
			"timestamp": 1690797504000,
			"last_trade_timestamp": 1690797504000,
			"status": "closed",
			"symbol": "USDT/AUD",
			"type": "limit",
			"time_in_force": "FOK",
			"side": "buy",
			"price": 21.1,
			"average": 2.98442,
			"amount": 101,
			"filled": 101,
			"remaining": 0,
			"cost": 301.426,
			"fee": 0,
			"error_message": "",
			"client_order_id": "",
			"trades": [
				{
					"id": "e1b3329c-4fc3-455a-82ed-ab5497b286cc",
					"timestamp": 1690797504000,
					"symbol": "USDT/AUD",
					"order": "e1af8ae0-3247-4755-87ad-f683fed3aff5",
					"type": "limit",
					"side": "buy",
					"taker_or_maker": "taker",
					"price": 21.1,
					"amount": 101,
					"cost": 301.426,
					"fee": 0
				}
			]
		}
	],
	"status": "success",
	"total": 14087,
	"page": 1
}

```

### <a id="4-request-for-quote"></a>4. Request for quote
<!--post https://sandbox-api.zerocap.com/v2/orders/request_for_quote-->

```

result = client.request_for_quote(
                    symbol='USDT/AUD', 
                    side='buy', 
                    amount='100', 
                    client_order_id='', 
                    )

```


Request parameters:

| Parameter       | required | data type | describe        | Value range |
|-----------------| -------- | --------- |-----------------| ----------- |
| symbol          | true     | string    | Symbol          | USDT/AUD    |
| side            | true     | string    | Side            | buy sell    |
| amount          | true     | string    | Amount          |             |
| client_order_id | false    | string    | Client Order ID |             |


Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
headers = {
    'api-key': api_key,
    'signature': signature,
    'Content-Type': 'application/json',
    'timestamp': str(timestamp),
}

data = 
{
    "symbol": "USDT/AUD",
    "side": "buy",
    "amount": "100",
    "client_order_id": ""
}


```

Response data:

| Parameter       | required | data type | describe        | Value range |
| --------------- | -------- | --------- |-----------------| ----------- |
| rfq_id          | true     | string    | RFQ ID          |             |
| client_order_id | true     | string    | Client Order ID |             |
| amount          | true     | float     | Amount          |             |
| side            | true     | string    | Side            |             |
| symbol          | true     | string    | Symbol          |             |
| price           | true     | float     | price           |             |
| created         | true     | string    | Side            |             |



Response example:

```

{
    "rfq_id": "3ec2f335-1995-4c59-9d3a-8e3a4d048d23",
    "client_order_id": "",
    "amount": 100.0,
    "side": "buy",
    "symbol": "USDT/AUD",
    "price": 1.4882,
    "created": "2024-01-15T02:49:17.658142Z"
}
```



### <a id="5-get-instruments"></a>5. Get instruments

<!--get https://sandbox-api.zerocap.com/v2/orders/get_instruments-->

```
result = client.get_instruments()
```

Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
headers = {
    'api-key': api_key,
    'signature': signature,
    'Content-Type': 'application/json',
    'timestamp': str(timestamp),
}
```

Response data:

| Parameter          | required | data type | describe           | Value range |
| ------------------ | -------- | --------- |--------------------| ----------- |
| name               | true     | string    | Name               |             |
| type               | true     | string    | Type               |             |
| is_tradable        | true     | bool      | Is Ttradable       |             |
| quantity_precision | true     | float     | Quantity Precision |             |
| max_quantity       | true     | float     | Max Quantity       |             |
| min_quantity       | true     | float     | Min Quantity       |             |
| price_precision    | true     | float     | Price Precision    |             |



Response example:

```
{
    "status": "successful",
    "instruments": [
        {
            "name": "USDT/AUD",
            "type": "SPOT",
            "is_tradable": false,
            "quantity_precision": 2.0,
            "max_quantity": 50000.0,
            "min_quantity": 1.0,
            "price_precision": 4.0
        }
    ]
}
```



### <a id="6-get-balances"></a>6. Get balances

<!--get https://sandbox-api.zerocap.com/v2/orders/get_balances-->

```
result = client.get_balances()

```

Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
headers = {
    'api-key': api_key,
    'signature': signature,
    'Content-Type': 'application/json',
    'timestamp': str(timestamp),
}
```

Response data:

| Parameter      | required  | data type | describe               | Value range |
|----------------|-----------|-----------|------------------------| ----------- |
| status         | true      | string    | Request status         |             |
| datetime       | true      | string    | Format time            |             |
| timestamp      | true      | int       | Millisecond timestamp  |             |




Response example:

```
{
    "status": "successful",
    "datetime": "2024-01-16T05:21:38Z",
    "timestamp": 1705382498277,
    "BTC": {"custody": {"free": 100, "total": 100, "used": 0}},
    "AUD": {
        "DiscountNote": {"free": 1080, "total": 1080, "used": 0},
        "YieldEntryNotes": {"free": 3, "total": 3, "used": 0},
        "custody": {"free": 3092.07032787879, "total": 3092.07032787879, "used": 0},
        "prime": {"free": 3024064.624523521, "total": 3024064.624523521, "used": 0},
        "prime_locked": {"free": 103.107987, "total": 103.107987, "used": 0},
    },
    "custody": {
        "CAD": {"free": 98111.56, "total": 98111.56, "used": 0},
        "ETH": {"free": 0, "total": 0, "used": 0},
    },
    "prime": {
        "AUD": {"free": 3024064.624523521, "total": 3024064.624523521, "used": 0},
        "BTC": {"free": 10.387466098753459, "total": 10.387466098753459, "used": 0},
    },
    "prime_locked": {
        "AUD": {"free": 103.107987, "total": 103.107987, "used": 0},
        "USDT": {"free": 12609.60579, "total": 12609.60579, "used": 0},
    },
    "savings": {
        "BTC": {"free": 0.1003151580555734, "total": 0.1003151580555734, "used": 0}
    },
    "staking": {"DIVI": {"free": 300, "total": 300, "used": 0}},
    "term_3M": {
        "USDT_ETH_TEST3_EA4E": {
            "free": 0.9939347875558201,
            "total": 0.9939347875558201,
            "used": 0,
        }
    },
    "term_6M": {
        "USDT_TRX_TEST4": {
            "free": 9.80542196309589,
            "total": 9.80542196309589,
            "used": 0,
        }
    },
    "unlock": {"BTC": {"free": 2.6246, "total": 2.6246, "used": 0}},
}
```


### <a id="7-get-transfers"></a>7. Get transfers

<!--get https://sandbox-api.zerocap.com/v2/orders/get_transfers-->

```
result = client.get_transfers(start_datetime=1706767455959,
                          end_datetime=1706770184310,
                          asset_id="ETH,AUD",
                          type="external_deposit,external_withdrawal",
                          page=1,
                          limit=20,
                          sort_order="desc")

```

Request parameters:

| Parameter      | required | data type | describe       | Value range                                                                                                                                 |
|----------------|----------|-----------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|
| start_datetime | false    | int       | Start Datetime |                                                                                                                                             |
| end_datetime   | false    | int       | End Datetime   |                                                                                                                                             |
| asset_id       | false    | string    | Asset Id       |                                                                                                                                             |
| type           | false    | string    | Type           | external_deposit,external_withdrawal,yield_deposit,create_term_yield,settled_term_yield, trading_transfer,trading_settlement,yield_interest,yield_withdrawal |
| page           | false    | int       | Page           |                                                                                                                                             |
| limit          | false    | int       | Limit          |                                                                                                                                             |
| sort_order     | false    | string    | Sort Order     | desc, asc                                                                                                                                   |



Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
headers = {
    'api-key': api_key,
    'signature': signature,
    'Content-Type': 'application/json',
    'timestamp': str(timestamp),
}

data = {
    "start_datetime": 1706767455959,
    "end_datetime": 1706770184310,
    "asset_id": "ETH,AUD",
    "type": "external_deposit,external_withdrawal",
    "page": 1,
    "limit": 20,
    "sort_order": "desc"
}
```

Response data:

| Parameter           | required | data type | describe                           | Value range |
|---------------------|----------|-----------|------------------------------------|-------------|
| status              | true     | string    | Interface request status.          |             |
| total               | true     | int       | The total number of matching data. |             |
| asset_id            | true     | string    | Asset ID                           |             |
| amount              | true     | float     | Amount                             |             |
| type                | true     | string    | Type                               |             |
| from_account_type   | true     | string    | From Account Type                  |             |
| to_account_type     | true     | string    | To Account Type                    |             |
| amount_usd          | true     | float     | Amount USD                         |             |
| interest_earned     | true     | string    | Interest Earned                    |             |
| tx_hash             | true     | string    | Tx Hash                            |             |
| source_address      | true     | string    | Source Address                     |             |
| destination_address | true     | string    | Destination Address                |             |
| tx_id               | true     | string    | Tx ID                              |             |
| fee                 | true     | float     | Fee                                |             |
| fee_currency        | true     | string    | Fee Currency                       |             |
| name_reference      | true     | string    | Name Reference                     |             |
| bank_account        | true     | string    | Bank Account                       |             |
| comment             | true     | string    | Comment                            |             |
| status              | true     | string    | Status                             |             |
| created_at          | true     | long      | Created At (milliseconds)          |             |





Response example:

```
{
    "status": "successful",
    "total": 78,
    "user_id": "",
    "fiat_transactions": [
        {
            "user_id": "",
            "asset_id": "AUD",
            "amount": 1.22,
            "type": "",
            "from_account_type": "",
            "to_account_type": "",
            "amount_usd": 0.8004786,
            "note": "",
            "source_address": "",
            "destination_address": "",
            "tx_id": "a7ff3ee8-d62e-413d-8bf1-1e1305604883auto_sweep",
            "fee": 0.0,
            "fee_currency": "",
            "comment": "",
            "status": "COMPLETED",
            "created_at": 1702267616000
        },
    ],
    "crypto_transactions": [
        {
            "user_id": "",
            "asset_id": "AUD",
            "amount": 1.22,
            "type": "",
            "from_account_type": "",
            "to_account_type": "",
            "amount_usd": 0.8004786,
            "interest_earned": 0.0,
            "note": "",
            "tx_hash": "",
            "source_address": "",
            "destination_address": "",
            "tx_id": "a7ff3ee8-d62e-413d-8bf1-1e1305604883auto_sweep",
            "fee": 0.0,
            "fee_currency": "",
            "comment": "",
            "status": "COMPLETED",
            "created_at": 1702267616000
        },
    ]
}
```



### <a id="8-get-time"></a>8. Get time

<!--get https://sandbox-api.zerocap.com/v2/systems/get_time-->

```
result = client.get_time()

```

Public endpoint: no parameters and no authentication headers (the client sends none). Use it to check your
clock before signing, since requests with a `timestamp` more than 30 s off are rejected with 408.

Response data:

| Parameter   | required | data type | describe                  | Value range |
|-------------|----------|-----------|---------------------------|-------------|
| server_time | true     | string    | Server time, Unix seconds |             |




Response example:

```
{
    "server_time": "1709654622"
}
```

### <a id="9-get-risk"></a>9. Get risk

<!--get https://sandbox-api.zerocap.com/v2/orders/get_risk-->

```
result = client.get_risk()

```

Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
headers = {
    'api-key': api_key,
    'signature': signature,
    'Content-Type': 'application/json',
    'timestamp': str(timestamp),
}
```

Response data:

| Parameter      | required | data type | describe       | Value range |
|----------------|----------|-----------|----------------| ----------- |
| max_risk_limit | true     | string    | Max Risk Limit |             |
| risk_exposure  | true     | string    | Risk Exposure  |             |
| equity         | true     | string    | Equity         |             |




Response example:

```
{
    "max_risk_limit": "100000",
    "risk_exposure": "0",
    "equity": "17048.35"
}
```


### <a id="10-request-for-firm-quote"></a>10. Request for firm quote

<!--post https://sandbox-api.zerocap.com/v2/orders/request_for_firm_quote-->

```
result = client.request_for_firm_quote(symbol='USDT/AUD', side='buy', amount='100', currency='', client_order_id='')
```

Returns a fixed-price quote with a `quote_id` (plus `client_order_id`, `amount`, `side`, `symbol`, `price`, `created`, `currency`, `base_amount`, `quote_amount` and `expires_at`, the expiry time in milliseconds). Pass the `quote_id` to `accept_quote` before `expires_at`.

### <a id="11-accept-quote"></a>11. Accept quote

<!--post https://sandbox-api.zerocap.com/v2/orders/accept_quote-->

```
result = client.accept_quote(quote_id='<quote_id from request_for_firm_quote>', client_order_id='')
```

Accepts a firm quote and creates a FOK order.

### <a id="12-get-external-wallets"></a>12. Get external wallets

<!--get https://sandbox-api.zerocap.com/v2/accounts/get_external_wallets-->

```
result = client.get_external_wallets()
```

Returns `external_wallets`: a list of `external_wallet_id, name, address, label, status, asset, network, asset_id`.

### <a id="13-initiate-withdrawal"></a>13. Initiate withdrawal

<!--post https://sandbox-api.zerocap.com/v2/accounts/initiate_withdrawal-->

```
result = client.initiate_withdrawal(asset_id='USDC', amount='100', external_wallet_id='<id from get_external_wallets>', from_account='trading', note='')
```

`from_account` is `trading` or `custody` (fiat supports `trading` only). The response contains `transfer` with `status` (PENDING, COMPLETED, REJECTED), `transaction_id` and `message`.

### <a id="14-initiate-transfer"></a>14. Initiate transfer

<!--post https://sandbox-api.zerocap.com/v2/accounts/initiate_transfer-->

```
result = client.initiate_transfer(asset_id='USDC', amount='100', from_account='trading', note='')
```

Moves funds between the custody and trading accounts; the response has the same shape as Initiate Withdrawal.

### <a id="errors"></a>Errors

Non-200 responses raise `zerocap_api.ZerocapAPIError` with `status_code` and `body`. Common cases: 401 (`Unauthorized.`, `encryption check is fail`, `Unauthorized IP address.`, `invalid timestamp` when `timestamp` is not an integer), 408 (timestamp skew over 30 s), 422 (validation, including characters outside the allowed set in `symbol`, `currency`, `asset_id` or `quote_id`), 429 (limit of 50 requests/second per endpoint per IP).


## <a id="websocket"></a>Websocket

The server pushes an unsolicited `{"type": "message", "message": "pong"}` roughly every 5 s. Send any message at least every 30 s
(the SDK's heartbeat thread sends one every 5 s); after 30 s of silence the server sends `{"type": "error", "error_code": 400, "error_message": "ping timeout"}`
and closes the connection. See https://docs.zerocap.com for the full Websocket Overview.
Order `trades` is always a list. `price` timestamps are float seconds; `order` and `trade` timestamps are integer milliseconds.

```
import time
from zerocap_api import ZerocapWebsocketClient

# API key and secret are required, please contact zerocap to register.

api_key = "***"
api_secret = "***"
websocket = ZerocapWebsocketClient(api_key, api_secret, env='sandbox')

# Create Ws Connection

timestamp = int(time.time())
websocket_connect = websocket.create_connection(timestamp)
connect_result = websocket.recv(websocket_connect)
print(connect_result)


```

`create_connection()` opens the socket and returns an iterator of raw messages. `recv(websocket_connect)` reads the
next message from that iterator; `recv()` with no argument reads straight from the live socket, so keeping the
iterator is optional. The connection stays open until `websocket.close()` or `websocket_connect.close()` is called;
calling `create_connection()` again closes the previous connection first.

Response example:

```
{
    "type": "message",
    "message":"Successfully connected."
}

{
    "type": "error",
    "error_code": 401,
    "error_message":"Unauthorized"
}

```



### <a id="1-subscribe-to-market-data"></a>1. Subscribe to market data


```

websocket.send({"type": 'price', "symbols": ["USDT/AUD"]})
while True:
    # Get messages
    message = websocket.recv(websocket_connect)
    print(f"Receiving message from server: \n{message}")

```


Request parameters:

| Parameter | required | data type | describe            | Value range |
|-----------|----------|-----------|---------------------|-------------|
| type      | true     | str       | Subscription type   | price       |
| symbols   | true     | list      | Transaction pairs   | ["USDT/AUD"] |
| is_subscribe | false | bool      | false to unsubscribe (default true) |         |


Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
header={"api-key": api_key, "signature": signature, "timestamp": str(timestamp)}

wss://dma-ws.zerocap.com/v2        # prod
wss://sandbox-ws.zerocap.com/v2    # sandbox

```


Response data:


| Parameter         | required | data type  | describe      | Value range           |
|-------------------|----------|------------|---------------|-----------------------|
| type              | true     | str        | type          | price, message, error |
| data              | false    | json       | data          |                       |
| data['timestamp'] | true     | float      | time (seconds) |                       |
| data['symbol']    | true     | str        | symbol        |                       |
| data['exchange']  | true     | str        | exchange      |                       |
| data['bids']      | true     | list       | bids          |                       |
| data['asks']      | true     | list       | asks          |                       |
| message           | false    | str        | description   |                       |
| error_code        | false    | int        | error code    |                       |
| error_message     | false    | str        | error message |                       |

Response example:

```
{
    "type": "error",
    "error_code": 400,
    "error_message":"invalid message"
}

{
    "type": "message",
    "message":"price Subscription successful."
}

{

    "type": "message",
    "message": "pong"
}

{
    "type": "price",
    "data": {
        'timestamp': 1692340935.4283478,
        'symbol': 'USDT/AUD',
        'exchange': 'zerocap',
        'bids': [
            [1.47386, 5000.0],
            [1.47238, 10000.0],
            [1.4709, 20000.0],
            [1.46941, 30000.0]
        ],
        'asks': [
            [1.57236, 5000.0],
            [1.57392, 10000.0],
            [1.57548, 20000.0],
            [1.57705, 30000.0]
        ]
    }
}

{
    "type": "message",
    "message":"Price stream unavailable."
}



```

### <a id="2-subscribe-to-order-updates"></a>2. Subscribe to order updates


```

websocket.send({"type": 'order'})
while True:
    # Get  messages
    message = websocket.recv(websocket_connect)
    
    print(f"Receiving message from server: \n{message}")

```


Request parameters:


| Parameter | required | data type | describe        | Value range |
|-----------|----------|-----------|-----------------|-------------|
| type      | true     | str       | Subscribed type | order       |


Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
header={"api-key": api_key, "signature": signature, "timestamp": str(timestamp)}

wss://dma-ws.zerocap.com/v2        # prod
wss://sandbox-ws.zerocap.com/v2    # sandbox

```


order Response data:


| Parameter                    | required  | data type | describe             | Value range           |
|------------------------------|-----------|-----------|----------------------|-----------------------|
| type                         | true      | str       | type                 | order, message, error |
| data                         | false     | json      | data                 |                       |
| data['id']                   | true      | str       | id                   |                       |
| data['timestamp']            | true      | long      | timestamp (ms)       |                       |
| data['last_trade_timestamp'] | true      | long      | last_trade_timestamp (ms) |                       |
| data['status']               | true      | str       | status               |                       |
| data['symbol']               | true      | str       | symbol               |                       |
| data['type']                 | true      | str       | type                 |                       |
| data['time_in_force']        | true      | str       | time_in_force        |                       |
| data['side']                 | true      | str       | side                 |                       |
| data['price']                | true      | float     | price                |                       |
| data['average']              | true      | float     | average              |                       |
| data['amount']               | true      | float     | amount               |                       |
| data['filled']               | true      | float     | filled               |                       |
| data['remaining']            | true      | float     | remaining            |                       |
| data['cost']                 | true      | float     | cost                 |                       |
| data['error_message']        | true      | str       | error_message        |                       |
| data['fee']                  | true      | float     | fee                  |                       |
| data['trades']               | true      | list      | trades               |                       |
| message                      | false     | str       | description          |                       |
| error_code                   | false     | int       | error code           |                       |
| error_message                | false     | str       | error message        |                       |


Response example:

```

{
    "type": "error",
    "error_code": 400,
    "error_message":"invalid message"
}

{
    "type": "message",
    "message":"order Subscription successful."
}

{
    "type": "message",
    "message": "pong"
}

{
"type":"order",
"data":{
    "id":"f7fc4e4f-c099-4b18-a231-f032adc8ae87",
    "timestamp":1692354083000,
    "last_trade_timestamp":1692354083000,
    "status":"open",
    "symbol":"USDT/AUD",
    "type":"limit",
    "time_in_force":"FOK",
    "side":"buy",
    "price":3,
    "average":0,
    "amount":3,
    "filled":0,
    "remaining":3,
    "cost":9,
    "error_message":"",
    "fee":0,
    "trades":[]
    }
}

{
"type":"order",
"data":{
    "id":"f7fc4e4f-c099-4b18-a231-f032adc8ae87",
    "timestamp":1692354083000,
    "last_trade_timestamp":1692354083000,
    "status":"closed",
    "symbol":"USDT/AUD",
    "type":"limit",
    "time_in_force":"FOK",
    "side":"buy",
    "price":3,
    "average":1.474899,
    "amount":3,
    "filled":3,
    "remaining":0,
    "cost":9,
    "error_message":"",
    "fee":0,
    "trades":[{
        "id":"fa9cbef5-54ce-4a3a-8115-3db8af1f1617",
        "timestamp":1692354083000,
        "symbol":"USDT/AUD",
        "order":"f7fc4e4f-c099-4b18-a231-f032adc8ae87",
        "type":"limit",
        "side":"buy",
        "taker_or_maker":"taker",
        "price":1.474899,
        "amount":3,
        "cost":4.424,
        "fee":0
        }]
    }
}


```

### <a id="3-subscribe-to-trades"></a>3. Subscribe to trades


```

websocket.send({"type": 'trade'})
while True:
    # Get  messages
    message = websocket.recv(websocket_connect)
    
    print(f"Receiving message from server: \n{message}")

```


Request parameters:


| Parameter | required | data type | describe        | Value range |
|-----------|----------|-----------|-----------------|-------------|
| type      | true     | str       | Subscribed type | trade       |


Request parameters: examples (cannot be used directly, you need to replace your own parameters)

```
header={"api-key": api_key, "signature": signature, "timestamp": str(timestamp)}

wss://dma-ws.zerocap.com/v2        # prod
wss://sandbox-ws.zerocap.com/v2    # sandbox

```

Transaction Record Response data:


| Parameter             | required | data type   | describe           | Value range          |
|-----------------------|----------|-------------|--------------------|----------------------|
| type                  | true     | str         | type               | trade,message, error |
| data                  | false    | json        | data               |                      |
| data['id']            | true     | str         | id                 |                      |
| data['timestamp']     | true     | long        | timestamp (ms)     |                      |
| data['symbol']        | true     | str         | symbol             |                      |
| data['order']         | true     | str         | order              |                      |
| data['type']          | true     | str         | type               |                      |
| data['side']          | true     | str         | side               |                      |
| data['taker_or_maker']  | true     | str         | taker_or_maker       |                      |
| data['price']         | true     | float       | price              |                      |
| data['amount']        | true     | float       | amount             |                      |
| data['cost']          | true     | float       | cost               |                      |
| data['fee']           | true     | float       | fee                |                      |
| data['client_order_id'] | true   | str         | client_order_id    |                      |
| message               | false    | str         | description        |                      |
| error_code            | false    | int         | error code         |                      |
| error_message         | false    | str         | error message      |                      |


Response example:

```

{
    "type": "error",
    "error_code": 400,
    "error_message":"invalid message"
}

{
    "type": "message",
    "message":"trade Subscription successful."
}

{

    "type": "message",
    "message": "pong"
}

{
"type":"trade",
"data":{
    "id":"fa9cbef5-54ce-4a3a-8115-3db8af1f1617",
    "timestamp":1692354083000,
    "symbol":"USDT/AUD",
    "order":"f7fc4e4f-c099-4b18-a231-f032adc8ae87",
    "type":"limit",
    "side":"buy",
    "taker_or_maker":"taker",
    "price":1.474899,
    "amount":3,
    "cost":4.424,
    "fee":0,
    "client_order_id":""
    }
}

```

## <a id="changelog"></a>Changelog

### 0.1.37
- `create_order` accepts `coinroutes_customer_id` again, as a deprecated keyword (default `None`, sent only when given), so integrations written for 0.1.35 and earlier keep working. 0.1.36 had removed it, which made those calls fail with `TypeError`.
- Websocket: discarding the iterator returned by `create_connection()` no longer closes the connection. The socket stays open until `close()` is called on the client or on the iterator, or a new `create_connection()` replaces it, so `create_connection()` followed by `recv()` / `send()` without arguments works.
- Packaging: wheel published alongside the sdist, `Source` and `Changelog` project links, Python 3.8 to 3.13 classifiers.
- README: introduction and table of contents, REST sample without unused imports, Get Time without authentication headers, integer `start_datetime`/`end_datetime`/`page`/`limit` in fetch_orders (and `page`/`limit` in get_transfers), sandbox websocket URL next to production.

### 0.1.36 (2026-10-16)
- Websocket errors keep their original type (e.g. `websockets.exceptions.ConnectionClosed`) instead of being wrapped in a bare `Exception`.
- `request_for_quote`: `symbol`, `side` and `amount` are required, matching the API.
- `create_order`: the `coinroutes_customer_id` argument is removed (it only applied to the Coinroutes integration and is no longer part of the public API).
- Order channel `trades` is always a list, and messages no longer carry `internal_extra_data` (server-side change).
- Fix websocket reconnect: breaking out of a `create_connection()` generator and calling `create_connection()` again no longer closes the new connection when the old generator is garbage collected. Each generator and heartbeat thread is now bound to its own socket, and a new `create_connection()` closes the previous connection first.

### 0.1.35 (2026-10-16)
- Only `prod` and `sandbox` environments are supported; unknown `env` raises `ValueError`.
- REST client: default 30 s timeout (`timeout=` argument), non-200 responses raise `ZerocapAPIError` (`status_code`, `body`), `price` is now required in `create_order`, `coinroutes_customer_id` is deprecated and only sent when set.
- New REST methods: `request_for_firm_quote`, `accept_quote`, `get_external_wallets`, `initiate_withdrawal`, `initiate_transfer`; `request_for_quote` gained `currency`.
- README: removed internal fields from examples (`counterparty`, `order_from`), snake_case field names, `amount` in request_for_quote, `crypto_transactions` in get_transfers, websocket `symbols`/`is_subscribe`/heartbeat notes.
- `get_time` is sent without authentication headers.
- Websocket client: socket opens in `create_connection()` (so `send()` works immediately), heartbeat thread is a daemon and stops on `close()`; `verify_identity()` removed.
