├── .DS_Store
├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── examples
└── readonly.py
├── luno_python
├── __init__.py
├── base_client.py
├── client.py
└── error.py
├── renovate.json5
├── setup.cfg
├── setup.py
└── tests
├── __init__.py
└── test_client.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luno/luno-python/9d604d4dda32626993b2dbdd748cdee526b60937/.DS_Store
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Run Tests
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | python-version: [ "3.12", "3.13" ]
17 |
18 | steps:
19 |
20 | - uses: actions/checkout@v4
21 |
22 | - name: Set up Python ${{ matrix.python-version }}
23 | uses: actions/setup-python@v5
24 | with:
25 | python-version: ${{ matrix.python-version }}
26 |
27 | - name: Install dependencies
28 | run: |
29 | python -m pip install --upgrade pip setuptools wheel
30 | pip install .[test]
31 |
32 | - name: Test with pytest
33 | run: |
34 | pytest
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | venv/
2 | *.pyc
3 | requirements.txt
4 | __pycache__/
5 | .pytest_cache/
6 | build/
7 | dist/
8 | *.egg-info/
9 | .eggs/
10 | .coverage
11 | .idea/
12 | /env/
13 | /.DS_Store
14 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Clone
4 | ```bash
5 | git clone https://github.com/luno/luno-python.git
6 | ```
7 | ## Create Virtual env
8 | ```bash
9 | cd luno-python
10 | python -m venv env
11 | source env/bin/activate
12 | ```
13 |
14 | ## Install Dependencies
15 | ```bash
16 | python -m pip install --upgrade pip setuptools wheel
17 | pip install -e '.[test]'
18 | ```
19 |
20 | ## Run Tests
21 | ```bash
22 | pytest
23 | ```
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Luno
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Luno API [](https://travis-ci.org/luno/luno-python)
4 |
5 | This Python package provides a wrapper for the [Luno API](https://www.luno.com/api).
6 |
7 | ### Installation
8 |
9 | ```
10 | pip install luno-python
11 | ```
12 |
13 | ### Authentication
14 |
15 | Please visit the [Settings](https://www.luno.com/wallet/settings/api_keys) page
16 | to generate an API key.
17 |
18 | ### Example usage
19 |
20 | ```python
21 | from luno_python.client import Client
22 |
23 | c = Client(api_key_id='key_id', api_key_secret='key_secret')
24 | try:
25 | res = c.get_ticker(pair='XBTZAR')
26 | print res
27 | except Exception as e:
28 | print e
29 | ```
30 |
31 | ### License
32 |
33 | [MIT](https://github.com/luno/luno-python/blob/master/LICENSE.txt)
34 |
35 |
--------------------------------------------------------------------------------
/examples/readonly.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 |
4 | from luno_python.client import Client
5 |
6 |
7 | if __name__ == '__main__':
8 | c = Client(api_key_id=os.getenv('LUNO_API_KEY_ID'),
9 | api_key_secret=os.getenv('LUNO_API_KEY_SECRET'))
10 |
11 | res = c.get_tickers()
12 | print(res)
13 | time.sleep(0.5)
14 |
15 | res = c.get_ticker(pair='XBTZAR')
16 | print(res)
17 | time.sleep(0.5)
18 |
19 | res = c.get_order_book(pair='XBTZAR')
20 | print(res)
21 | time.sleep(0.5)
22 |
23 | since = int(time.time()*1000)-24*60*59*1000
24 | res = c.list_trades(pair='XBTZAR', since=since)
25 | print(res)
26 | time.sleep(0.5)
27 |
28 | res = c.get_candles(pair='XBTZAR', since=since, duration=300)
29 | print(res)
30 | time.sleep(0.5)
31 |
32 | res = c.get_balances()
33 | print(res)
34 | time.sleep(0.5)
35 |
36 | aid = ''
37 | if res['balance']:
38 | aid = res['balance'][0]['account_id']
39 |
40 | if aid:
41 | res = c.list_transactions(id=aid, min_row=1, max_row=10)
42 | print(res)
43 | time.sleep(0.5)
44 |
45 | if aid:
46 | res = c.list_pending_transactions(id=aid)
47 | print(res)
48 | time.sleep(0.5)
49 |
50 | res = c.list_orders()
51 | print(res)
52 | time.sleep(0.5)
53 |
54 | res = c.list_user_trades(pair='XBTZAR')
55 | print(res)
56 | time.sleep(0.5)
57 |
58 | res = c.get_fee_info(pair='XBTZAR')
59 | print(res)
60 | time.sleep(0.5)
61 |
62 | res = c.get_funding_address(asset='XBT')
63 | print(res)
64 | time.sleep(0.5)
65 |
66 | res = c.list_withdrawals()
67 | print(res)
68 | time.sleep(0.5)
69 |
70 | wid = ''
71 | if res['withdrawals']:
72 | wid = res['withdrawals'][0]['id']
73 |
74 | if wid:
75 | res = c.get_withdrawal(id=wid)
76 | print(res)
77 | time.sleep(0.5)
78 |
--------------------------------------------------------------------------------
/luno_python/__init__.py:
--------------------------------------------------------------------------------
1 | VERSION = '0.0.10'
2 |
--------------------------------------------------------------------------------
/luno_python/base_client.py:
--------------------------------------------------------------------------------
1 | import json
2 | import platform
3 | import requests
4 | import six
5 |
6 | try:
7 | from json.decoder import JSONDecodeError
8 | except ImportError:
9 | JSONDecodeError = ValueError
10 |
11 | from . import VERSION
12 | from .error import APIError
13 |
14 | DEFAULT_BASE_URL = 'https://api.luno.com'
15 | DEFAULT_TIMEOUT = 10
16 | PYTHON_VERSION = platform.python_version()
17 | SYSTEM = platform.system()
18 | ARCH = platform.machine()
19 |
20 |
21 | class BaseClient:
22 | def __init__(self, base_url='', timeout=0,
23 | api_key_id='', api_key_secret=''):
24 | """
25 | :type base_url: str
26 | :type timeout: float
27 | :type api_key_id: str
28 | :type api_key_secret: str
29 | """
30 | self.set_auth(api_key_id, api_key_secret)
31 | self.set_base_url(base_url)
32 | self.set_timeout(timeout)
33 |
34 | self.session = requests.Session()
35 |
36 | def set_auth(self, api_key_id, api_key_secret):
37 | """Provides the client with an API key and secret.
38 |
39 | :type api_key_id: str
40 | :type api_key_secret: str
41 | """
42 | self.api_key_id = api_key_id
43 | self.api_key_secret = api_key_secret
44 |
45 | def set_base_url(self, base_url):
46 | """Overrides the default base URL. For internal use.
47 |
48 | :type base_url: str
49 | """
50 | if base_url == '':
51 | base_url = DEFAULT_BASE_URL
52 | self.base_url = base_url.rstrip('/')
53 |
54 | def set_timeout(self, timeout):
55 | """Sets the timeout, in seconds, for requests made by the client.
56 |
57 | :type timeout: float
58 | """
59 | if timeout == 0:
60 | timeout = DEFAULT_TIMEOUT
61 | self.timeout = timeout
62 |
63 | def do(self, method, path, req=None, auth=False):
64 | """Performs an API request and returns the response.
65 |
66 | TODO: Handle 429s
67 |
68 | :type method: str
69 | :type path: str
70 | :type req: object
71 | :type auth: bool
72 | """
73 | try:
74 | params = json.loads(json.dumps(req))
75 | except Exception:
76 | params = None
77 | headers = {'User-Agent': self.make_user_agent()}
78 | args = dict(timeout=self.timeout, params=params, headers=headers)
79 | if auth:
80 | args['auth'] = (self.api_key_id, self.api_key_secret)
81 | url = self.make_url(path, params)
82 | res = self.session.request(method, url, **args)
83 | try:
84 | e = res.json()
85 | if 'error' in e and 'error_code' in e:
86 | raise APIError(e['error_code'], e['error'])
87 | return e
88 | except JSONDecodeError:
89 | raise Exception('luno: unknown API error (%s)' % res.status_code)
90 |
91 | def make_url(self, path, params):
92 | """
93 | :type path: str
94 | :rtype: str
95 | """
96 | if params:
97 | for k, v in six.iteritems(params):
98 | path = path.replace('{' + k + '}', str(v))
99 | return self.base_url + '/' + path.lstrip('/')
100 |
101 | def make_user_agent(self):
102 | """
103 | :rtype: str
104 | """
105 | return "LunoPythonSDK/%s python/%s %s %s" % \
106 | (VERSION, PYTHON_VERSION, SYSTEM, ARCH)
107 |
--------------------------------------------------------------------------------
/luno_python/client.py:
--------------------------------------------------------------------------------
1 | from .base_client import BaseClient
2 |
3 |
4 | class Client(BaseClient):
5 | """
6 | Python SDK for the Luno API.
7 |
8 | Example usage:
9 |
10 | from luno_python.client import Client
11 |
12 |
13 | c = Client(api_key_id='key_id', api_key_secret='key_secret')
14 | try:
15 | res = c.get_ticker(pair='XBTZAR')
16 | print res
17 | except Exception as e:
18 | print e
19 | """
20 |
21 | def cancel_withdrawal(self, id):
22 | """Makes a call to DELETE /api/1/withdrawals/{id}.
23 |
24 | Cancels a withdrawal request.
25 | This can only be done if the request is still in state
PENDING
.
26 |
27 | Permissions required: Perm_W_Withdrawals
28 |
29 | :param id: ID of the withdrawal to cancel.
30 | :type id: int
31 | """
32 | req = {
33 | 'id': id,
34 | }
35 | return self.do('DELETE', '/api/1/withdrawals/{id}', req=req, auth=True)
36 |
37 | def create_account(self, currency, name):
38 | """Makes a call to POST /api/1/accounts.
39 |
40 | This request creates an Account for the specified currency. Please note that the balances for the Account will be displayed based on the asset
value, which is the currency the Account is based on.
41 |
42 | Permissions required: Perm_W_Addresses
43 |
44 | :param currency: The currency code for the Account you want to create. Please see the Currency section for a detailed list of currencies supported by the Luno platform.
45 |
46 | Users must be verified to trade currency in order to be able to create an Account. For more information on the verification process, please see How do I verify my identity?.
47 |
48 | Users have a limit of 10 accounts per currency.
49 | :type currency: str
50 | :param name: The label to use for this account
51 | :type name: str
52 | """
53 | req = {
54 | 'currency': currency,
55 | 'name': name,
56 | }
57 | return self.do('POST', '/api/1/accounts', req=req, auth=True)
58 |
59 | def create_beneficiary(self, account_type, bank_account_number, bank_name, bank_recipient):
60 | """Makes a call to POST /api/1/beneficiaries.
61 |
62 | Create a new beneficiary.
63 |
64 | Permissions required: Perm_W_Beneficiaries
65 |
66 | :param account_type: Bank account type
67 | :type account_type: str
68 | :param bank_account_number: Beneficiary bank account number
69 | :type bank_account_number: str
70 | :param bank_name: Bank SWIFT code
71 | :type bank_name: str
72 | :param bank_recipient: The owner of the recipient account
73 | :type bank_recipient: str
74 | """
75 | req = {
76 | 'account_type': account_type,
77 | 'bank_account_number': bank_account_number,
78 | 'bank_name': bank_name,
79 | 'bank_recipient': bank_recipient,
80 | }
81 | return self.do('POST', '/api/1/beneficiaries', req=req, auth=True)
82 |
83 | def create_funding_address(self, asset, account_id=None, name=None):
84 | """Makes a call to POST /api/1/funding_address.
85 |
86 | Allocates a new receive address to your account. There is a rate limit of 1
87 | address per hour, but bursts of up to 10 addresses are allowed. Only 1
88 | Ethereum receive address can be created.
89 |
90 | Permissions required: Perm_W_Addresses
91 |
92 | :param asset: Currency code of the asset.
93 | :type asset: str
94 | :param account_id: An optional account_id to assign the new Receive Address too
95 | :type account_id: int
96 | :param name: An optional name for the new Receive Address
97 | :type name: str
98 | """
99 | req = {
100 | 'asset': asset,
101 | 'account_id': account_id,
102 | 'name': name,
103 | }
104 | return self.do('POST', '/api/1/funding_address', req=req, auth=True)
105 |
106 | def create_withdrawal(self, amount, type, beneficiary_id=None, external_id=None, fast=None, reference=None):
107 | """Makes a call to POST /api/1/withdrawals.
108 |
109 | Creates a new withdrawal request to the specified beneficiary.
110 |
111 | Permissions required: Perm_W_Withdrawals
112 |
113 | :param amount: Amount to withdraw. The currency withdrawn depends on the type setting.
114 | :type amount: float
115 | :param type: Withdrawal method.
116 | :type type: str
117 | :param beneficiary_id: The beneficiary ID of the bank account the withdrawal will be paid out to.
118 | This parameter is required if the user has set up multiple beneficiaries.
119 | The beneficiary ID can be found by selecting on the beneficiary name on the user’s Beneficiaries page.
120 | :type beneficiary_id: int
121 | :param external_id: Optional unique ID to associate with this withdrawal.
122 | Useful to prevent duplicate sends.
123 | This field supports all alphanumeric characters including "-" and "_".
124 | :type external_id: str
125 | :param fast: If true, it will be a fast withdrawal if possible. Fast withdrawals come with a fee.
126 | Currently fast withdrawals are only available for `type=ZAR_EFT`; for other types, an error is returned.
127 | Fast withdrawals are not possible for Bank of Baroda, Deutsche Bank, Merrill Lynch South Africa, UBS, Postbank and Tyme Bank.
128 | The fee to be charged is the same as when withdrawing from the UI.
129 | :type fast: bool
130 | :param reference: For internal use.
131 | Deprecated: We don't allow custom references and will remove this soon.
132 | :type reference: str
133 | """
134 | req = {
135 | 'amount': amount,
136 | 'type': type,
137 | 'beneficiary_id': beneficiary_id,
138 | 'external_id': external_id,
139 | 'fast': fast,
140 | 'reference': reference,
141 | }
142 | return self.do('POST', '/api/1/withdrawals', req=req, auth=True)
143 |
144 | def delete_beneficiary(self, id):
145 | """Makes a call to DELETE /api/1/beneficiaries/{id}.
146 |
147 | Delete a beneficiary
148 |
149 | Permissions required: Perm_W_Beneficiaries
150 |
151 | :param id: ID of the Beneficiary to delete.
152 | :type id: int
153 | """
154 | req = {
155 | 'id': id,
156 | }
157 | return self.do('DELETE', '/api/1/beneficiaries/{id}', req=req, auth=True)
158 |
159 | def get_balances(self, assets=None):
160 | """Makes a call to GET /api/1/balance.
161 |
162 | The list of all Accounts and their respective balances for the requesting user.
163 |
164 | Permissions required: Perm_R_Balance
165 |
166 | :param assets: Only return balances for wallets with these currencies (if not provided,
167 | all balances will be returned). To request balances for multiple currencies,
168 | pass the parameter multiple times,
169 | e.g. `assets=XBT&assets=ETH`.
170 | :type assets: list
171 | """
172 | req = {
173 | 'assets': assets,
174 | }
175 | return self.do('GET', '/api/1/balance', req=req, auth=True)
176 |
177 | def get_candles(self, duration, pair, since):
178 | """Makes a call to GET /api/exchange/1/candles.
179 |
180 | Get candlestick market data from the specified time until now, from the oldest to the most recent.
181 |
182 | Permissions required: MP_None
183 |
184 | :param duration: Candle duration in seconds.
185 | For example, 300 corresponds to 5m candles. Currently supported
186 | durations are: 60 (1m), 300 (5m), 900 (15m), 1800 (30m), 3600 (1h),
187 | 10800 (3h), 14400 (4h), 28800 (8h), 86400 (24h), 259200 (3d), 604800
188 | (7d).
189 | :type duration: int
190 | :param pair: Currency pair
191 | :type pair: str
192 | :param since: Filter to candles starting on or after this timestamp (Unix milliseconds).
193 | Only up to 1000 of the earliest candles are returned.
194 | :type since: int
195 | """
196 | req = {
197 | 'duration': duration,
198 | 'pair': pair,
199 | 'since': since,
200 | }
201 | return self.do('GET', '/api/exchange/1/candles', req=req, auth=True)
202 |
203 | def get_fee_info(self, pair):
204 | """Makes a call to GET /api/1/fee_info.
205 |
206 | Returns the fees and 30 day trading volume (as of midnight) for a given currency pair. For complete details, please see Fees & Features.
207 |
208 | Permissions required: Perm_R_Orders
209 |
210 | :param pair: Get fee information about this pair.
211 | :type pair: str
212 | """
213 | req = {
214 | 'pair': pair,
215 | }
216 | return self.do('GET', '/api/1/fee_info', req=req, auth=True)
217 |
218 | def get_funding_address(self, asset, address=None):
219 | """Makes a call to GET /api/1/funding_address.
220 |
221 | Returns the default receive address associated with your account and the
222 | amount received via the address. Users can specify an optional address parameter to return information for a non-default receive address.
223 | In the response, total_received
is the total confirmed amount received excluding unconfirmed transactions.
224 | total_unconfirmed
is the total sum of unconfirmed receive transactions.
225 |
226 | Permissions required: Perm_R_Addresses
227 |
228 | :param asset: Currency code of the asset.
229 | :type asset: str
230 | :param address: Specific cryptocurrency address to retrieve. If not provided, the
231 | default address will be used.
232 | :type address: str
233 | """
234 | req = {
235 | 'asset': asset,
236 | 'address': address,
237 | }
238 | return self.do('GET', '/api/1/funding_address', req=req, auth=True)
239 |
240 | def get_move(self, client_move_id=None, id=None):
241 | """Makes a call to GET /api/exchange/1/move.
242 |
243 | Get a specific move funds instruction by either id
or
244 | client_move_id
. If both are provided an API error will be
245 | returned.
246 |
247 | Permissions required: MP_None
248 |
249 | :param client_move_id: Get by the user defined ID. This is mutually exclusive with id
and is required if id
is
250 | not provided.
251 | :type client_move_id: str
252 | :param id: Get by the system ID. This is mutually exclusive with client_move_id
and is required if
253 | client_move_id
is not provided.
254 | :type id: str
255 | """
256 | req = {
257 | 'client_move_id': client_move_id,
258 | 'id': id,
259 | }
260 | return self.do('GET', '/api/exchange/1/move', req=req, auth=True)
261 |
262 | def get_order(self, id):
263 | """Makes a call to GET /api/1/orders/{id}.
264 |
265 | Get an Order's details by its ID.
266 |
267 | Permissions required: Perm_R_Orders
268 |
269 | :param id: Order reference
270 | :type id: str
271 | """
272 | req = {
273 | 'id': id,
274 | }
275 | return self.do('GET', '/api/1/orders/{id}', req=req, auth=True)
276 |
277 | def get_order_book(self, pair):
278 | """Makes a call to GET /api/1/orderbook_top.
279 |
280 | This request returns the best 100 `bids` and `asks`, for the currency pair specified, in the Order Book.
281 |
282 | `asks` are sorted by price ascending and `bids` are sorted by price descending.
283 |
284 | Multiple orders at the same price are aggregated.
285 |
286 | :param pair: Currency pair of the Orders to retrieve
287 | :type pair: str
288 | """
289 | req = {
290 | 'pair': pair,
291 | }
292 | return self.do('GET', '/api/1/orderbook_top', req=req, auth=False)
293 |
294 | def get_order_book_full(self, pair):
295 | """Makes a call to GET /api/1/orderbook.
296 |
297 | This request returns all `bids` and `asks`, for the currency pair specified, in the Order Book.
298 |
299 | `asks` are sorted by price ascending and `bids` are sorted by price descending.
300 |
301 | Multiple orders at the same price are not aggregated.
302 |
303 | WARNING: This may return a large amount of data.
304 | Users are recommended to use the top 100 bids and asks
305 | or the Streaming API.
306 |
307 | :param pair: Currency pair of the Orders to retrieve
308 | :type pair: str
309 | """
310 | req = {
311 | 'pair': pair,
312 | }
313 | return self.do('GET', '/api/1/orderbook', req=req, auth=False)
314 |
315 | def get_order_v2(self, id):
316 | """Makes a call to GET /api/exchange/2/orders/{id}.
317 |
318 | Get the details for an order.
319 |
320 | Permissions required: Perm_R_Orders
321 |
322 | :param id: Order reference
323 | :type id: str
324 | """
325 | req = {
326 | 'id': id,
327 | }
328 | return self.do('GET', '/api/exchange/2/orders/{id}', req=req, auth=True)
329 |
330 | def get_order_v3(self, client_order_id=None, id=None):
331 | """Makes a call to GET /api/exchange/3/order.
332 |
333 | Get the details for an order by order reference or client order ID.
334 | Exactly one of the two parameters must be provided, otherwise an error is returned.
335 | Permissions required: Perm_R_Orders
336 |
337 | :param client_order_id: Client Order ID has the value that was passed in when the Order was posted.
338 | :type client_order_id: str
339 | :param id: Order reference
340 | :type id: str
341 | """
342 | req = {
343 | 'client_order_id': client_order_id,
344 | 'id': id,
345 | }
346 | return self.do('GET', '/api/exchange/3/order', req=req, auth=True)
347 |
348 | def get_ticker(self, pair):
349 | """Makes a call to GET /api/1/ticker.
350 |
351 | Returns the latest ticker indicators for the specified currency pair.
352 |
353 | Please see the Currency list for the complete list of supported currency pairs.
354 |
355 | :param pair: Currency pair
356 | :type pair: str
357 | """
358 | req = {
359 | 'pair': pair,
360 | }
361 | return self.do('GET', '/api/1/ticker', req=req, auth=False)
362 |
363 | def get_tickers(self, pair=None):
364 | """Makes a call to GET /api/1/tickers.
365 |
366 | Returns the latest ticker indicators from all active Luno exchanges.
367 |
368 | Please see the Currency list for the complete list of supported currency pairs.
369 |
370 | :param pair: Return tickers for multiple markets (if not provided, all tickers will be returned).
371 | To request tickers for multiple markets, pass the parameter multiple times,
372 | e.g. `pair=XBTZAR&pair=ETHZAR`.
373 | :type pair: list
374 | """
375 | req = {
376 | 'pair': pair,
377 | }
378 | return self.do('GET', '/api/1/tickers', req=req, auth=False)
379 |
380 | def get_withdrawal(self, id):
381 | """Makes a call to GET /api/1/withdrawals/{id}.
382 |
383 | Returns the status of a particular withdrawal request.
384 |
385 | Permissions required: Perm_R_Withdrawals
386 |
387 | :param id: Withdrawal ID to retrieve.
388 | :type id: int
389 | """
390 | req = {
391 | 'id': id,
392 | }
393 | return self.do('GET', '/api/1/withdrawals/{id}', req=req, auth=True)
394 |
395 | def list_beneficiaries(self, bank_recipient=None):
396 | """Makes a call to GET /api/1/beneficiaries.
397 |
398 | Returns a list of bank beneficiaries.
399 |
400 | Permissions required: Perm_R_Beneficiaries
401 |
402 | :param bank_recipient: :type bank_recipient: str
403 | """
404 | req = {
405 | 'bank_recipient': bank_recipient,
406 | }
407 | return self.do('GET', '/api/1/beneficiaries', req=req, auth=True)
408 |
409 | def list_moves(self, before=None, limit=None):
410 | """Makes a call to GET /api/exchange/1/move/list_moves.
411 |
412 | Returns a list of the most recent moves ordered from newest to oldest.
413 | This endpoint will list up to 100 most recent moves by default.
414 |
415 | Permissions required: MP_None
416 |
417 | :param before: Filter to moves requested before this timestamp (Unix milliseconds)
418 | :type before: int
419 | :param limit: Limit to this many moves
420 | :type limit: int
421 | """
422 | req = {
423 | 'before': before,
424 | 'limit': limit,
425 | }
426 | return self.do('GET', '/api/exchange/1/move/list_moves', req=req, auth=True)
427 |
428 | def list_orders(self, created_before=None, limit=None, pair=None, state=None):
429 | """Makes a call to GET /api/1/listorders.
430 |
431 | Returns a list of the most recently placed Orders.
432 | Users can specify an optional state=PENDING
parameter to restrict the results to only open Orders.
433 | Users can also specify the market by using the optional currency pair parameter.
434 |
435 | Permissions required: Perm_R_Orders
436 |
437 | :param created_before: Filter to orders created before this timestamp (Unix milliseconds)
438 | :type created_before: int
439 | :param limit: Limit to this many orders
440 | :type limit: int
441 | :param pair: Filter to only orders of this currency pair
442 | :type pair: str
443 | :param state: Filter to only orders of this state
444 | :type state: str
445 | """
446 | req = {
447 | 'created_before': created_before,
448 | 'limit': limit,
449 | 'pair': pair,
450 | 'state': state,
451 | }
452 | return self.do('GET', '/api/1/listorders', req=req, auth=True)
453 |
454 | def list_orders_v2(self, closed=None, created_before=None, limit=None, pair=None):
455 | """Makes a call to GET /api/exchange/2/listorders.
456 |
457 | Returns a list of the most recently placed orders ordered from newest to
458 | oldest. This endpoint will list up to 100 most recent open orders by
459 | default.
460 |
461 | Please note: This data is archived 100 days after an exchange order is completed.
462 |
463 | Permissions required: Perm_R_Orders
464 |
465 | :param closed: If true, will return closed orders instead of open orders.
466 | :type closed: bool
467 | :param created_before: Filter to orders created before this timestamp (Unix milliseconds)
468 | :type created_before: int
469 | :param limit: Limit to this many orders
470 | :type limit: int
471 | :param pair: Filter to only orders of this currency pair.
472 | :type pair: str
473 | """
474 | req = {
475 | 'closed': closed,
476 | 'created_before': created_before,
477 | 'limit': limit,
478 | 'pair': pair,
479 | }
480 | return self.do('GET', '/api/exchange/2/listorders', req=req, auth=True)
481 |
482 | def list_pending_transactions(self, id):
483 | """Makes a call to GET /api/1/accounts/{id}/pending.
484 |
485 | Return a list of all transactions that have not completed for the Account.
486 |
487 | Pending transactions are not numbered, and may be reordered, deleted or updated at any time.
488 |
489 | Permissions required: Perm_R_Transactions
490 |
491 | :param id: Account ID
492 | :type id: int
493 | """
494 | req = {
495 | 'id': id,
496 | }
497 | return self.do('GET', '/api/1/accounts/{id}/pending', req=req, auth=True)
498 |
499 | def list_trades(self, pair, since=None):
500 | """Makes a call to GET /api/1/trades.
501 |
502 | Returns a list of recent trades for the specified currency pair. At most
503 | 100 trades are returned per call and never trades older than 24h. The
504 | trades are sorted from newest to oldest.
505 |
506 | Please see the Currency list for the complete list of supported currency pairs.
507 |
508 | :param pair: Currency pair of the market to list the trades from
509 | :type pair: str
510 | :param since: Fetch trades executed after this time, specified as a Unix timestamp in
511 | milliseconds. An error will be returned if this is before 24h ago. Use
512 | this parameter to either restrict to a shorter window or to iterate over
513 | the trades in case you need more than the 100 most recent trades.
514 | :type since: int
515 | """
516 | req = {
517 | 'pair': pair,
518 | 'since': since,
519 | }
520 | return self.do('GET', '/api/1/trades', req=req, auth=False)
521 |
522 | def list_transactions(self, id, max_row, min_row):
523 | """Makes a call to GET /api/1/accounts/{id}/transactions.
524 |
525 | Return a list of transaction entries from an account.
526 |
527 | Transaction entry rows are numbered sequentially starting from 1, where 1 is
528 | the oldest entry. The range of rows to return are specified with the
529 | min_row
(inclusive) and max_row
(exclusive)
530 | parameters. At most 1000 rows can be requested per call.
531 |
532 | If min_row
or max_row
is non-positive, the range
533 | wraps around the most recent row. For example, to fetch the 100 most recent
534 | rows, use min_row=-100
and max_row=0
.
535 |
536 | Permissions required: Perm_R_Transactions
537 |
538 | :param id: Account ID - the unique identifier for the specific Account.
539 | :type id: int
540 | :param max_row: Maximum of the row range to return (exclusive)
541 | :type max_row: int
542 | :param min_row: Minimum of the row range to return (inclusive)
543 | :type min_row: int
544 | """
545 | req = {
546 | 'id': id,
547 | 'max_row': max_row,
548 | 'min_row': min_row,
549 | }
550 | return self.do('GET', '/api/1/accounts/{id}/transactions', req=req, auth=True)
551 |
552 | def list_transfers(self, account_id, before=None, limit=None):
553 | """Makes a call to GET /api/exchange/1/transfers.
554 |
555 | Returns a list of the most recent confirmed transfers ordered from newest to
556 | oldest.
557 | This includes bank transfers, card payments, or on-chain transactions that
558 | have been reflected on your account available balance.
559 |
560 | Note that the Transfer `amount` is always a positive value and you should
561 | use the `inbound` flag to determine the direction of the transfer.
562 |
563 | If you need to paginate the results you can set the `before` parameter to
564 | the last returned transfer `created_at` field value and repeat the request
565 | until you have all the transfers you need.
566 | This endpoint will list up to 100 transfers at a time by default.
567 |
568 | Permissions required: Perm_R_Transfers
569 |
570 | :param account_id: Unique identifier of the account to list the transfers from.
571 | :type account_id: int
572 | :param before: Filter to transfers created before this timestamp (Unix milliseconds).
573 | The default value (0) will return the latest transfers on the account.
574 | :type before: int
575 | :param limit: Limit to this many transfers.
576 | :type limit: int
577 | """
578 | req = {
579 | 'account_id': account_id,
580 | 'before': before,
581 | 'limit': limit,
582 | }
583 | return self.do('GET', '/api/exchange/1/transfers', req=req, auth=True)
584 |
585 | def list_user_trades(self, pair, after_seq=None, before=None, before_seq=None, limit=None, since=None, sort_desc=None):
586 | """Makes a call to GET /api/1/listtrades.
587 |
588 | Returns a list of the recent Trades for a given currency pair for this user, sorted by oldest first.
589 | If before
is specified, then Trades are returned sorted by most-recent first.
590 |
591 | type
in the response indicates the type of Order that was placed to participate in the trade.
592 | Possible types: BID
, ASK
.
593 |
594 | If is_buy
in the response is true, then the Order which completed the trade (market taker) was a Bid Order.
595 |
596 | Results of this query may lag behind the latest data.
597 |
598 | Permissions required: Perm_R_Orders
599 |
600 | :param pair: Filter to trades of this currency pair.
601 | :type pair: str
602 | :param after_seq: Filter to trades from (including) this sequence number.
603 | Default behaviour is not to include this filter.
604 | :type after_seq: int
605 | :param before: Filter to trades before this timestamp (Unix milliseconds).
606 | :type before: int
607 | :param before_seq: Filter to trades before (excluding) this sequence number.
608 | Default behaviour is not to include this filter.
609 | :type before_seq: int
610 | :param limit: Limit to this number of trades (default 100).
611 | :type limit: int
612 | :param since: Filter to trades on or after this timestamp (Unix milliseconds).
613 | :type since: int
614 | :param sort_desc: If set to true, sorts trades in descending order, otherwise ascending
615 | order will be assumed.
616 | :type sort_desc: bool
617 | """
618 | req = {
619 | 'pair': pair,
620 | 'after_seq': after_seq,
621 | 'before': before,
622 | 'before_seq': before_seq,
623 | 'limit': limit,
624 | 'since': since,
625 | 'sort_desc': sort_desc,
626 | }
627 | return self.do('GET', '/api/1/listtrades', req=req, auth=True)
628 |
629 | def list_withdrawals(self, before_id=None, limit=None):
630 | """Makes a call to GET /api/1/withdrawals.
631 |
632 | Returns a list of withdrawal requests.
633 |
634 | Permissions required: Perm_R_Withdrawals
635 |
636 | :param before_id: Filter to withdrawals requested on or before the withdrawal with this ID.
637 | Can be used for pagination.
638 | :type before_id: int
639 | :param limit: Limit to this many withdrawals
640 | :type limit: int
641 | """
642 | req = {
643 | 'before_id': before_id,
644 | 'limit': limit,
645 | }
646 | return self.do('GET', '/api/1/withdrawals', req=req, auth=True)
647 |
648 | def markets(self, pair=None):
649 | """Makes a call to GET /api/exchange/1/markets.
650 |
651 | List all supported markets parameter information like price scale, min and
652 | max order volumes and market ID.
653 |
654 | :param pair: List of market pairs to return. Requesting only the required pairs will improve response times.
655 | :type pair: list
656 | """
657 | req = {
658 | 'pair': pair,
659 | }
660 | return self.do('GET', '/api/exchange/1/markets', req=req, auth=False)
661 |
662 | def move(self, amount, credit_account_id, debit_account_id, client_move_id=None):
663 | """Makes a call to POST /api/exchange/1/move.
664 |
665 | Move funds between two of your transactional accounts with the same currency
666 | The funds may not be moved by the time the request returns. The GET method
667 | can be used to poll for the move's status.
668 |
669 | Note: moves will show as transactions, but not as transfers.
670 |
671 | Permissions required: MP_None_Write
672 |
673 | :param amount: Amount to transfer. Must be positive.
674 | :type amount: float
675 | :param credit_account_id: The account to credit the funds to.
676 | :type credit_account_id: int
677 | :param debit_account_id: The account to debit the funds from.
678 | :type debit_account_id: int
679 | :param client_move_id: Client move ID.
680 | May only contain alphanumeric (0-9, a-z, or A-Z) and special characters (_ ; , . -). Maximum length: 255.
681 | It will be available in read endpoints, so you can use it to avoid duplicate moves between the same accounts.
682 | Values must be unique across all your successful calls of this endpoint; trying to create a move request
683 | with the same `client_move_id` as one of your past move requests will result in a HTTP 409 Conflict response.
684 | :type client_move_id: str
685 | """
686 | req = {
687 | 'amount': amount,
688 | 'credit_account_id': credit_account_id,
689 | 'debit_account_id': debit_account_id,
690 | 'client_move_id': client_move_id,
691 | }
692 | return self.do('POST', '/api/exchange/1/move', req=req, auth=True)
693 |
694 | def post_limit_order(self, pair, price, type, volume, base_account_id=None, client_order_id=None, counter_account_id=None, post_only=None, stop_direction=None, stop_price=None, time_in_force=None, timestamp=None, ttl=None):
695 | """Makes a call to POST /api/1/postorder.
696 |
697 | Warning! Orders cannot be reversed once they have executed.
698 | Please ensure your program has been thoroughly tested before submitting Orders.
699 |
700 | If no base_account_id
or counter_account_id
are specified,
701 | your default base currency or counter currency account will be used.
702 | You can find your Account IDs by calling the Balances API.
703 |
704 | Permissions required: Perm_W_Orders
705 |
706 | :param pair: The currency pair to trade.
707 | :type pair: str
708 | :param price: Limit price as a decimal string in units of ZAR/BTC.
709 | :type price: float
710 | :param type: BID
for a bid (buy) limit order
711 | ASK
for an ask (sell) limit order
712 | :type type: str
713 | :param volume: Amount of cryptocurrency to buy or sell as a decimal string in units of the currency.
714 | :type volume: float
715 | :param base_account_id: The base currency Account to use in the trade.
716 | :type base_account_id: int
717 | :param client_order_id: Client order ID.
718 | May only contain alphanumeric (0-9, a-z, or A-Z) and special characters (_ ; , . -). Maximum length: 255.
719 | It will be available in read endpoints, so you can use it to reconcile Luno with your internal system.
720 | Values must be unique across all your successful order creation endpoint calls; trying to create an order
721 | with the same `client_order_id` as one of your past orders will result in a HTTP 409 Conflict response.
722 | :type client_order_id: str
723 | :param counter_account_id: The counter currency Account to use in the trade.
724 | :type counter_account_id: int
725 | :param post_only: Post-only Orders will be cancelled if they would otherwise have traded
726 | immediately.
727 | For example, if there's a bid at ZAR 100,000 and you place a post-only ask at ZAR 100,000,
728 | your order will be cancelled instead of trading.
729 | If the best bid is ZAR 100,000 and you place a post-only ask at ZAR 101,000,
730 | your order won't trade but will go into the order book.
731 | :type post_only: bool
732 | :param stop_direction: Side of the trigger price to activate the order. This should be set if `stop_price` is also
733 | set.
734 |
735 | `RELATIVE_LAST_TRADE` will automatically infer the direction based on the last
736 | trade price and the stop price. If last trade price is less than stop price then stop
737 | direction is ABOVE otherwise is BELOW.
738 | :type stop_direction: str
739 | :param stop_price: Trigger trade price to activate this order as a decimal string. If this
740 | is set then this is treated as a Stop Limit Order and `stop_direction`
741 | is expected to be set too.
742 | :type stop_price: float
743 | :param time_in_force: GTC
Good 'Til Cancelled. The order remains open until it is filled or cancelled by the user.
744 | IOC
Immediate Or Cancel. The part of the order that cannot be filled immediately will be cancelled. Cannot be post-only.
745 | FOK
Fill Or Kill. If the order cannot be filled immediately and completely it will be cancelled before any trade. Cannot be post-only.
746 | :type time_in_force: str
747 | :param timestamp: Unix timestamp in milliseconds of when the request was created and sent.
748 | :type timestamp: int
749 | :param ttl: Specifies the number of milliseconds after timestamp the request is valid for.
750 | If `timestamp` is not specified, `ttl` will not be used.
751 | :type ttl: int
752 | """
753 | req = {
754 | 'pair': pair,
755 | 'price': price,
756 | 'type': type,
757 | 'volume': volume,
758 | 'base_account_id': base_account_id,
759 | 'client_order_id': client_order_id,
760 | 'counter_account_id': counter_account_id,
761 | 'post_only': post_only,
762 | 'stop_direction': stop_direction,
763 | 'stop_price': stop_price,
764 | 'time_in_force': time_in_force,
765 | 'timestamp': timestamp,
766 | 'ttl': ttl,
767 | }
768 | return self.do('POST', '/api/1/postorder', req=req, auth=True)
769 |
770 | def post_market_order(self, pair, type, base_account_id=None, base_volume=None, client_order_id=None, counter_account_id=None, counter_volume=None, timestamp=None, ttl=None):
771 | """Makes a call to POST /api/1/marketorder.
772 |
773 | A Market Order executes immediately, and either buys as much of the asset that can be bought for a set amount of fiat currency, or sells a set amount of the asset for as much as possible.
774 |
775 | Warning! Orders cannot be reversed once they have executed.
776 | Please ensure your program has been thoroughly tested before submitting Orders.
777 |
778 | If no base_account_id
or counter_account_id
are specified, the default base currency or counter currency account will be used.
779 | Users can find their account IDs by calling the Balances request.
780 |
781 | Permissions required: Perm_W_Orders
782 |
783 | :param pair: The currency pair to trade.
784 | :type pair: str
785 | :param type: BUY
to buy an asset
786 | SELL
to sell an asset
787 | :type type: str
788 | :param base_account_id: The base currency account to use in the trade.
789 | :type base_account_id: int
790 | :param base_volume: For a SELL
order: amount of the base currency to use (e.g. how much BTC to sell for EUR in the BTC/EUR market)
791 | :type base_volume: float
792 | :param client_order_id: Client order ID.
793 | May only contain alphanumeric (0-9, a-z, or A-Z) and special characters (_ ; , . -). Maximum length: 255.
794 | It will be available in read endpoints, so you can use it to reconcile Luno with your internal system.
795 | Values must be unique across all your successful order creation endpoint calls; trying to create an order
796 | with the same `client_order_id` as one of your past orders will result in a HTTP 409 Conflict response.
797 | :type client_order_id: str
798 | :param counter_account_id: The counter currency account to use in the trade.
799 | :type counter_account_id: int
800 | :param counter_volume: For a BUY
order: amount of the counter currency to use (e.g. how much EUR to use to buy BTC in the BTC/EUR market)
801 | :type counter_volume: float
802 | :param timestamp: Unix timestamp in milliseconds of when the request was created and sent.
803 | :type timestamp: int
804 | :param ttl: Specifies the number of milliseconds after timestamp the request is valid for.
805 | If `timestamp` is not specified, `ttl` will not be used.
806 | :type ttl: int
807 | """
808 | req = {
809 | 'pair': pair,
810 | 'type': type,
811 | 'base_account_id': base_account_id,
812 | 'base_volume': base_volume,
813 | 'client_order_id': client_order_id,
814 | 'counter_account_id': counter_account_id,
815 | 'counter_volume': counter_volume,
816 | 'timestamp': timestamp,
817 | 'ttl': ttl,
818 | }
819 | return self.do('POST', '/api/1/marketorder', req=req, auth=True)
820 |
821 | def send(self, address, amount, currency, account_id=None, description=None, destination_tag=None, external_id=None, forex_notice_self_declaration=None, has_destination_tag=None, is_drb=None, is_forex_send=None, memo=None, message=None):
822 | """Makes a call to POST /api/1/send.
823 |
824 | Send assets from an Account. Please note that the asset type sent must match the receive address of the same cryptocurrency of the same type - Bitcoin to Bitcoin, Ethereum to Ethereum, etc.
825 |
826 | Sends can be made to cryptocurrency receive addresses.
827 |
828 | Note: This is currently unavailable to users who are verified in countries with money travel rules.
829 |
830 | Permissions required: Perm_W_Send
831 |
832 | :param address: Destination address or email address.
833 |
834 | Note:
835 |
MP_None
895 |
896 | :param address: Destination address or email address.
897 |
898 | Note:
899 | Perm_W_Orders
926 |
927 | :param order_id: The Order identifier as a string.
928 | :type order_id: str
929 | """
930 | req = {
931 | 'order_id': order_id,
932 | }
933 | return self.do('POST', '/api/1/stoporder', req=req, auth=True)
934 |
935 | def update_account_name(self, id, name):
936 | """Makes a call to PUT /api/1/accounts/{id}/name.
937 |
938 | Update the name of an account with a given ID.
939 |
940 | Permissions required: Perm_W_Addresses
941 |
942 | :param id: Account ID - the unique identifier for the specific Account.
943 | :type id: int
944 | :param name: The label to use for this account
945 | :type name: str
946 | """
947 | req = {
948 | 'id': id,
949 | 'name': name,
950 | }
951 | return self.do('PUT', '/api/1/accounts/{id}/name', req=req, auth=True)
952 |
953 | def validate(self, address, currency, address_name=None, beneficiary_name=None, country=None, date_of_birth=None, destination_tag=None, has_destination_tag=None, institution_name=None, is_legal_entity=None, is_private_wallet=None, is_self_send=None, memo=None, nationality=None, physical_address=None, wallet_name=None):
954 | """Makes a call to POST /api/1/address/validate.
955 |
956 | Validate receive addresses, to which a customer wishes to make cryptocurrency sends, are verified under covering
957 | regulatory requirements for the customer such as travel rules.
958 |
959 | Permissions required: Perm_W_Send
960 |
961 | :param address: Destination address or email address.
962 |
963 | Note:
964 |