afp-sdk 0.5.2__py3-none-any.whl → 0.5.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- afp/api/trading.py +100 -7
- afp/enums.py +1 -0
- afp/exceptions.py +4 -0
- afp/exchange.py +28 -0
- afp/schemas.py +12 -1
- {afp_sdk-0.5.2.dist-info → afp_sdk-0.5.4.dist-info}/METADATA +1 -1
- {afp_sdk-0.5.2.dist-info → afp_sdk-0.5.4.dist-info}/RECORD +9 -9
- {afp_sdk-0.5.2.dist-info → afp_sdk-0.5.4.dist-info}/WHEEL +1 -1
- {afp_sdk-0.5.2.dist-info → afp_sdk-0.5.4.dist-info}/licenses/LICENSE +0 -0
afp/api/trading.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import secrets
|
|
2
2
|
import warnings
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
4
|
from decimal import Decimal
|
|
5
5
|
from typing import Generator, Iterable
|
|
6
6
|
|
|
@@ -16,6 +16,7 @@ from ..schemas import (
|
|
|
16
16
|
Intent,
|
|
17
17
|
IntentData,
|
|
18
18
|
MarketDepthData,
|
|
19
|
+
OHLCVItem,
|
|
19
20
|
Order,
|
|
20
21
|
OrderFilter,
|
|
21
22
|
OrderCancellationData,
|
|
@@ -123,8 +124,9 @@ class Trading(ExchangeAPI):
|
|
|
123
124
|
|
|
124
125
|
Raises
|
|
125
126
|
------
|
|
126
|
-
afp.exceptions.
|
|
127
|
-
If the exchange rejects the
|
|
127
|
+
afp.exceptions.RateLimitExceeded
|
|
128
|
+
If the exchange rejects the order because of too many requests from the
|
|
129
|
+
authenticated account.
|
|
128
130
|
"""
|
|
129
131
|
submission = OrderSubmission(
|
|
130
132
|
type=OrderType.LIMIT_ORDER,
|
|
@@ -146,8 +148,13 @@ class Trading(ExchangeAPI):
|
|
|
146
148
|
|
|
147
149
|
Raises
|
|
148
150
|
------
|
|
151
|
+
afp.exceptions.NotFoundError
|
|
152
|
+
If the exchange rejects the cancellation because the intent does not exist.
|
|
153
|
+
afp.exceptions.RateLimitExceeded
|
|
154
|
+
If the exchange rejects the cancellation because of too many requests from
|
|
155
|
+
the authenticated account.
|
|
149
156
|
afp.exceptions.ValidationError
|
|
150
|
-
If the exchange rejects the cancellation.
|
|
157
|
+
If the exchange rejects the cancellation because it is invalid.
|
|
151
158
|
"""
|
|
152
159
|
nonce = self._generate_nonce()
|
|
153
160
|
cancellation_hash = hashing.generate_order_cancellation_hash(nonce, intent_hash)
|
|
@@ -238,6 +245,7 @@ class Trading(ExchangeAPI):
|
|
|
238
245
|
self,
|
|
239
246
|
*,
|
|
240
247
|
product_id: str | None = None,
|
|
248
|
+
intent_account_id: str | None = None,
|
|
241
249
|
type_: str | None = None,
|
|
242
250
|
states: Iterable[str] = (),
|
|
243
251
|
side: str | None = None,
|
|
@@ -255,6 +263,8 @@ class Trading(ExchangeAPI):
|
|
|
255
263
|
Parameters
|
|
256
264
|
----------
|
|
257
265
|
product_id : str, optional
|
|
266
|
+
intent_account_id : str, optional
|
|
267
|
+
Defaults to the address of the authenticated account.
|
|
258
268
|
type_ : str, optional
|
|
259
269
|
One of `LIMIT_ORDER` and `CANCEL_ORDER`.
|
|
260
270
|
states : iterable of str
|
|
@@ -274,8 +284,11 @@ class Trading(ExchangeAPI):
|
|
|
274
284
|
-------
|
|
275
285
|
list of afp.schemas.OrderFill
|
|
276
286
|
"""
|
|
287
|
+
if intent_account_id is None:
|
|
288
|
+
intent_account_id = self._authenticator.address
|
|
289
|
+
|
|
277
290
|
filter = OrderFilter(
|
|
278
|
-
intent_account_id=
|
|
291
|
+
intent_account_id=intent_account_id,
|
|
279
292
|
product_id=product_id,
|
|
280
293
|
type=None if type_ is None else OrderType(type_.upper()),
|
|
281
294
|
states=[OrderState(state.upper()) for state in states],
|
|
@@ -306,6 +319,7 @@ class Trading(ExchangeAPI):
|
|
|
306
319
|
self,
|
|
307
320
|
*,
|
|
308
321
|
product_id: str | None = None,
|
|
322
|
+
intent_account_id: str | None = None,
|
|
309
323
|
intent_hash: str | None = None,
|
|
310
324
|
start: datetime | None = None,
|
|
311
325
|
end: datetime | None = None,
|
|
@@ -323,6 +337,8 @@ class Trading(ExchangeAPI):
|
|
|
323
337
|
Parameters
|
|
324
338
|
----------
|
|
325
339
|
product_id : str, optional
|
|
340
|
+
intent_account_id : str, optional
|
|
341
|
+
Defaults to the address of the authenticated account.
|
|
326
342
|
intent_hash : str, optional
|
|
327
343
|
start : datetime.datetime, optional
|
|
328
344
|
end : datetime.datetime, optional
|
|
@@ -339,8 +355,11 @@ class Trading(ExchangeAPI):
|
|
|
339
355
|
-------
|
|
340
356
|
list of afp.schemas.OrderFill
|
|
341
357
|
"""
|
|
358
|
+
if intent_account_id is None:
|
|
359
|
+
intent_account_id = self._authenticator.address
|
|
360
|
+
|
|
342
361
|
filter = OrderFillFilter(
|
|
343
|
-
intent_account_id=
|
|
362
|
+
intent_account_id=intent_account_id,
|
|
344
363
|
product_id=product_id,
|
|
345
364
|
intent_hash=intent_hash,
|
|
346
365
|
start=start,
|
|
@@ -357,6 +376,7 @@ class Trading(ExchangeAPI):
|
|
|
357
376
|
self,
|
|
358
377
|
*,
|
|
359
378
|
product_id: str | None = None,
|
|
379
|
+
intent_account_id: str | None = None,
|
|
360
380
|
intent_hash: str | None = None,
|
|
361
381
|
trade_states: Iterable[str] = ("PENDING",),
|
|
362
382
|
) -> Generator[OrderFill, None, None]:
|
|
@@ -376,6 +396,8 @@ class Trading(ExchangeAPI):
|
|
|
376
396
|
Parameters
|
|
377
397
|
----------
|
|
378
398
|
product_id : str, optional
|
|
399
|
+
intent_account_id : str, optional
|
|
400
|
+
Defaults to the address of the authenticated account.
|
|
379
401
|
intent_hash : str, optional
|
|
380
402
|
trade_states: iterable of str
|
|
381
403
|
Any of `PENDING`, `CLEARED` and `REJECTED`.
|
|
@@ -384,8 +406,11 @@ class Trading(ExchangeAPI):
|
|
|
384
406
|
-------
|
|
385
407
|
afp.schemas.OrderFill
|
|
386
408
|
"""
|
|
409
|
+
if intent_account_id is None:
|
|
410
|
+
intent_account_id = self._authenticator.address
|
|
411
|
+
|
|
387
412
|
filter = OrderFillFilter(
|
|
388
|
-
intent_account_id=
|
|
413
|
+
intent_account_id=intent_account_id,
|
|
389
414
|
product_id=product_id,
|
|
390
415
|
intent_hash=intent_hash,
|
|
391
416
|
start=None,
|
|
@@ -439,3 +464,71 @@ class Trading(ExchangeAPI):
|
|
|
439
464
|
"""
|
|
440
465
|
value = validators.validate_hexstr32(product_id)
|
|
441
466
|
yield from self._exchange.iter_market_depth_data(value)
|
|
467
|
+
|
|
468
|
+
def ohlcv(
|
|
469
|
+
self,
|
|
470
|
+
product_id: str,
|
|
471
|
+
start: datetime | None = None,
|
|
472
|
+
interval: timedelta = timedelta(minutes=5),
|
|
473
|
+
) -> list[OHLCVItem]:
|
|
474
|
+
"""Retrieves Open-High-Low-Close-Volume time series data for the given product.
|
|
475
|
+
|
|
476
|
+
Parameters
|
|
477
|
+
----------
|
|
478
|
+
product_id : str
|
|
479
|
+
start : datetime
|
|
480
|
+
Defaults to 1 day ago.
|
|
481
|
+
interval : timedelta
|
|
482
|
+
The distance between 2 data points. Gets rounded to a multiple of 5 seconds.
|
|
483
|
+
Defaults to 5 minutes.
|
|
484
|
+
|
|
485
|
+
Returns
|
|
486
|
+
-------
|
|
487
|
+
list of afp.schemas.OHLCVItem
|
|
488
|
+
|
|
489
|
+
Raises
|
|
490
|
+
------
|
|
491
|
+
afp.exceptions.NotFoundError
|
|
492
|
+
If no such product exists.
|
|
493
|
+
"""
|
|
494
|
+
if start is None:
|
|
495
|
+
start = datetime.now() - timedelta(days=1)
|
|
496
|
+
|
|
497
|
+
product_id = validators.validate_hexstr32(product_id)
|
|
498
|
+
start_timestamp = int(start.timestamp())
|
|
499
|
+
interval_secs = int(validators.validate_timedelta(interval).total_seconds())
|
|
500
|
+
return self._exchange.get_time_series_data(
|
|
501
|
+
product_id, start_timestamp, interval_secs
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
def iter_ohlcv(
|
|
505
|
+
self, product_id: str, interval: timedelta = timedelta(seconds=5)
|
|
506
|
+
) -> Generator[OHLCVItem, None, None]:
|
|
507
|
+
"""Subscribes to Open-High-Low-Close-Volume time series data updates for the
|
|
508
|
+
given product.
|
|
509
|
+
|
|
510
|
+
Returns a generator that yields OHLCV data points as they are published
|
|
511
|
+
by the exhange.
|
|
512
|
+
|
|
513
|
+
Parameters
|
|
514
|
+
----------
|
|
515
|
+
product_id : str
|
|
516
|
+
interval : timedelta
|
|
517
|
+
The distance between 2 data points. Gets rounded to a multiple of 5 seconds.
|
|
518
|
+
Defaults to 5 seconds.
|
|
519
|
+
|
|
520
|
+
Yields
|
|
521
|
+
-------
|
|
522
|
+
afp.schemas.OHLCVItem
|
|
523
|
+
|
|
524
|
+
Raises
|
|
525
|
+
------
|
|
526
|
+
afp.exceptions.NotFoundError
|
|
527
|
+
If no such product exists.
|
|
528
|
+
"""
|
|
529
|
+
product_id = validators.validate_hexstr32(product_id)
|
|
530
|
+
start_timestamp = int(datetime.now().timestamp())
|
|
531
|
+
interval_secs = int(validators.validate_timedelta(interval).total_seconds())
|
|
532
|
+
yield from self._exchange.iter_time_series_data(
|
|
533
|
+
product_id, start_timestamp, interval_secs
|
|
534
|
+
)
|
afp/enums.py
CHANGED
afp/exceptions.py
CHANGED
afp/exchange.py
CHANGED
|
@@ -11,6 +11,7 @@ from .exceptions import (
|
|
|
11
11
|
AuthorizationError,
|
|
12
12
|
ExchangeError,
|
|
13
13
|
NotFoundError,
|
|
14
|
+
RateLimitExceeded,
|
|
14
15
|
ValidationError,
|
|
15
16
|
)
|
|
16
17
|
from .schemas import (
|
|
@@ -21,6 +22,7 @@ from .schemas import (
|
|
|
21
22
|
ExchangeProductUpdateSubmission,
|
|
22
23
|
LoginSubmission,
|
|
23
24
|
MarketDepthData,
|
|
25
|
+
OHLCVItem,
|
|
24
26
|
Order,
|
|
25
27
|
OrderFilter,
|
|
26
28
|
OrderFill,
|
|
@@ -138,6 +140,30 @@ class ExchangeClient:
|
|
|
138
140
|
for line in response.iter_lines():
|
|
139
141
|
yield MarketDepthData.model_validate_json(line)
|
|
140
142
|
|
|
143
|
+
# GET /time-series/{product_id}
|
|
144
|
+
def get_time_series_data(
|
|
145
|
+
self, product_id: str, start: int, interval: int
|
|
146
|
+
) -> list[OHLCVItem]:
|
|
147
|
+
response = self._send_request(
|
|
148
|
+
"GET",
|
|
149
|
+
f"/time-series/{product_id}",
|
|
150
|
+
params=dict(start=start, interval=interval),
|
|
151
|
+
)
|
|
152
|
+
return [OHLCVItem(**item) for item in response.json()["data"]]
|
|
153
|
+
|
|
154
|
+
# GET /stream/time-series/{product_id}
|
|
155
|
+
def iter_time_series_data(
|
|
156
|
+
self, product_id: str, start: int, interval: int
|
|
157
|
+
) -> Generator[OHLCVItem, None, None]:
|
|
158
|
+
response = self._send_request(
|
|
159
|
+
"GET",
|
|
160
|
+
f"/stream/time-series/{product_id}",
|
|
161
|
+
params=dict(start=start, interval=interval),
|
|
162
|
+
stream=True,
|
|
163
|
+
)
|
|
164
|
+
for line in response.iter_lines():
|
|
165
|
+
yield OHLCVItem.model_validate_json(line)
|
|
166
|
+
|
|
141
167
|
def _send_request(
|
|
142
168
|
self,
|
|
143
169
|
method: str,
|
|
@@ -173,6 +199,8 @@ class ExchangeClient:
|
|
|
173
199
|
raise AuthorizationError(http_error) from http_error
|
|
174
200
|
if http_error.response.status_code == requests.codes.NOT_FOUND:
|
|
175
201
|
raise NotFoundError(http_error) from http_error
|
|
202
|
+
if http_error.response.status_code == requests.codes.TOO_MANY_REQUESTS:
|
|
203
|
+
raise RateLimitExceeded(http_error) from http_error
|
|
176
204
|
if http_error.response.status_code == requests.codes.BAD_REQUEST:
|
|
177
205
|
try:
|
|
178
206
|
reason = response.json()["detail"]
|
afp/schemas.py
CHANGED
|
@@ -78,7 +78,8 @@ class LoginSubmission(Model):
|
|
|
78
78
|
|
|
79
79
|
class ExchangeParameters(Model):
|
|
80
80
|
trading_protocol_id: str
|
|
81
|
-
|
|
81
|
+
maker_trading_fee_rate: Decimal
|
|
82
|
+
taker_trading_fee_rate: Decimal
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
# Admin API
|
|
@@ -182,6 +183,7 @@ class OrderFill(Model):
|
|
|
182
183
|
trade: Trade
|
|
183
184
|
quantity: int
|
|
184
185
|
price: Decimal
|
|
186
|
+
trading_fee_rate: Decimal
|
|
185
187
|
|
|
186
188
|
|
|
187
189
|
class OrderFillFilter(PaginationFilter):
|
|
@@ -209,6 +211,15 @@ class MarketDepthData(Model):
|
|
|
209
211
|
asks: list[MarketDepthItem]
|
|
210
212
|
|
|
211
213
|
|
|
214
|
+
class OHLCVItem(Model):
|
|
215
|
+
timestamp: Timestamp
|
|
216
|
+
open: Decimal
|
|
217
|
+
high: Decimal
|
|
218
|
+
low: Decimal
|
|
219
|
+
close: Decimal
|
|
220
|
+
volume: int
|
|
221
|
+
|
|
222
|
+
|
|
212
223
|
# Clearing API
|
|
213
224
|
|
|
214
225
|
|
|
@@ -5,7 +5,7 @@ afp/api/admin.py,sha256=6TiWCo0SB41CtXYTkTYYGQY7MrdB6OMTyU3-0J0V_7o,2170
|
|
|
5
5
|
afp/api/base.py,sha256=5X1joEwFX4gxYUDCSTvp_hSFCfLZCktnW-UtZFxDquk,4471
|
|
6
6
|
afp/api/margin_account.py,sha256=sC1DF7J3QTHR7cXcRjTG33oZswVy6qr9xnevQbrQ-ew,15256
|
|
7
7
|
afp/api/product.py,sha256=2N4bPBahuw19GcBbBL20oQ5RFRuUxa5LOZzmN7hs39U,10156
|
|
8
|
-
afp/api/trading.py,sha256=
|
|
8
|
+
afp/api/trading.py,sha256=RvsMjPBiuwyB64SsiVAUgKXFRazodq-iht6FdlgSX1A,17649
|
|
9
9
|
afp/auth.py,sha256=sV_9E6CgRWV1xYoppc4IdrnqNo5ZNDBIp6QF3fQbMWE,2055
|
|
10
10
|
afp/bindings/__init__.py,sha256=_n9xoogYi8AAlSx_PE-wjnwP1ujVyDUwoRM0BSm243U,1271
|
|
11
11
|
afp/bindings/auctioneer_facet.py,sha256=4p906zdU2lUsqpWlsiLE3dlxTPrlNpqk8DtjiQUWJ8M,23919
|
|
@@ -24,14 +24,14 @@ afp/bindings/trading_protocol.py,sha256=ZloF3REbjFq9v0UGVsM0_Lk0EhfWJKdeJ0PzVEny
|
|
|
24
24
|
afp/config.py,sha256=_WKywiuty8poE1A0v46uBe1JGpfCzRlxCPamKennfpE,699
|
|
25
25
|
afp/constants.py,sha256=EvDhLpKBOsc8OHGm1paiUAdAetPGD4nyi13coB8rd14,1930
|
|
26
26
|
afp/decorators.py,sha256=SEUQtbgPGc4iVPtBQV2eiCejcDAVImmXcI0uPXFhtJA,2774
|
|
27
|
-
afp/enums.py,sha256=
|
|
28
|
-
afp/exceptions.py,sha256=
|
|
29
|
-
afp/exchange.py,sha256
|
|
27
|
+
afp/enums.py,sha256=9JhwdLcTNhyKabKdrALAlCeL3C5XfeXYSSSXSCsuTzE,688
|
|
28
|
+
afp/exceptions.py,sha256=sgPOzrqbB0u8Kc6N65j5_0y4owyyq4oU6-5idcJO7Rs,441
|
|
29
|
+
afp/exchange.py,sha256=auy5SRNaf1PdsqY4HU8w4tAe1KPb_sAGsTW-K-HW76o,7673
|
|
30
30
|
afp/hashing.py,sha256=gBCWN93-ydRPlgnnorSvDQlylcnglrAypRDb-1K-20I,1949
|
|
31
31
|
afp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
-
afp/schemas.py,sha256=
|
|
32
|
+
afp/schemas.py,sha256=sCARcUvUNEZMcR2nn3iWrygAaoYqv038cLI9EXJFG9A,6818
|
|
33
33
|
afp/validators.py,sha256=zQvPu3HDu6ADnEE72EJlS5hc1-xfru78Mzd74s1u_EM,1841
|
|
34
|
-
afp_sdk-0.5.
|
|
35
|
-
afp_sdk-0.5.
|
|
36
|
-
afp_sdk-0.5.
|
|
37
|
-
afp_sdk-0.5.
|
|
34
|
+
afp_sdk-0.5.4.dist-info/licenses/LICENSE,sha256=ZdaKItgc2ppfqta2OJV0oHpSJiK87PUxmUkUo-_0SB8,1065
|
|
35
|
+
afp_sdk-0.5.4.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
36
|
+
afp_sdk-0.5.4.dist-info/METADATA,sha256=dGqKdpflOR9xCeYhMMZHzCcXzwgM0ItpP8NJFOj2C_0,6354
|
|
37
|
+
afp_sdk-0.5.4.dist-info/RECORD,,
|
|
File without changes
|