afp-sdk 0.5.1__py3-none-any.whl → 0.5.3__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 CHANGED
@@ -1,25 +1,28 @@
1
1
  import secrets
2
- from datetime import datetime
2
+ import warnings
3
+ from datetime import datetime, timedelta
3
4
  from decimal import Decimal
4
- from typing import Generator
5
+ from typing import Generator, Iterable
5
6
 
6
7
  from web3 import Web3
7
8
 
8
9
  from .. import hashing, validators
10
+ from ..constants import DEFAULT_BATCH_SIZE
9
11
  from ..decorators import refresh_token_on_expiry
10
- from ..enums import OrderType
12
+ from ..enums import OrderSide, OrderState, OrderType, TradeState
11
13
  from ..schemas import (
12
14
  ExchangeProduct,
15
+ ExchangeProductFilter,
13
16
  Intent,
14
17
  IntentData,
15
18
  MarketDepthData,
19
+ OHLCVItem,
16
20
  Order,
21
+ OrderFilter,
17
22
  OrderCancellationData,
18
23
  OrderFill,
19
24
  OrderFillFilter,
20
- OrderSide,
21
25
  OrderSubmission,
22
- TradeState,
23
26
  )
24
27
  from .base import ExchangeAPI
25
28
 
@@ -61,6 +64,7 @@ class Trading(ExchangeAPI):
61
64
  ----------
62
65
  product : afp.schemas.ExchangeProduct
63
66
  side : str
67
+ One of `BID` and `ASK`.
64
68
  limit_price : decimal.Decimal
65
69
  quantity : decimal.Decimal
66
70
  max_trading_fee_rate : decimal.Decimal
@@ -87,7 +91,7 @@ class Trading(ExchangeAPI):
87
91
  ),
88
92
  quantity=quantity,
89
93
  max_trading_fee_rate=max_trading_fee_rate,
90
- side=getattr(OrderSide, side.upper()),
94
+ side=OrderSide(side.upper()),
91
95
  good_until_time=good_until_time,
92
96
  nonce=self._generate_nonce(),
93
97
  )
@@ -161,14 +165,34 @@ class Trading(ExchangeAPI):
161
165
  )
162
166
  return self._exchange.submit_order(submission)
163
167
 
164
- def products(self) -> list[ExchangeProduct]:
168
+ def products(
169
+ self,
170
+ batch: int = 1,
171
+ batch_size: int = DEFAULT_BATCH_SIZE,
172
+ newest_first: bool = True,
173
+ ) -> list[ExchangeProduct]:
165
174
  """Retrieves the products approved for trading on the exchange.
166
175
 
176
+ If there are more than `batch_size` number of products then they can be queried
177
+ in batches.
178
+
179
+ Parameters
180
+ ----------
181
+ batch : int, optional
182
+ 1-based index of the batch of products.
183
+ batch_size : int, optional
184
+ The maximum number of products in one batch.
185
+ newest_first : bool, optional
186
+ Whether to sort products in descending or ascending order by creation time.
187
+
167
188
  Returns
168
189
  -------
169
190
  list of afp.schemas.ExchangeProduct
170
191
  """
171
- return self._exchange.get_approved_products()
192
+ filter = ExchangeProductFilter(
193
+ batch=batch, batch_size=batch_size, newest_first=newest_first
194
+ )
195
+ return self._exchange.get_approved_products(filter)
172
196
 
173
197
  def product(self, product_id: str) -> ExchangeProduct:
174
198
  """Retrieves a product for trading by its ID.
@@ -211,53 +235,133 @@ class Trading(ExchangeAPI):
211
235
  return self._exchange.get_order_by_id(value)
212
236
 
213
237
  @refresh_token_on_expiry
214
- def open_orders(self, product_id: str | None = None) -> list[Order]:
215
- """Retrieves all open and partially filled limit orders that have been submitted
216
- by the authenticated account.
238
+ def orders(
239
+ self,
240
+ *,
241
+ product_id: str | None = None,
242
+ intent_account_id: str | None = None,
243
+ type_: str | None = None,
244
+ states: Iterable[str] = (),
245
+ side: str | None = None,
246
+ start: datetime | None = None,
247
+ end: datetime | None = None,
248
+ batch: int = 1,
249
+ batch_size: int = DEFAULT_BATCH_SIZE,
250
+ newest_first: bool = True,
251
+ ) -> list[Order]:
252
+ """Retrieves the authenticated account's orders that match the given parameters.
253
+
254
+ If there are more than `batch_size` number of orders then they can be queried
255
+ in batches.
217
256
 
218
257
  Parameters
219
258
  ----------
220
259
  product_id : str, optional
260
+ intent_account_id : str, optional
261
+ Defaults to the address of the authenticated account.
262
+ type_ : str, optional
263
+ One of `LIMIT_ORDER` and `CANCEL_ORDER`.
264
+ states : iterable of str
265
+ Any of `RECEIVED`, `PENDING`, `OPEN`, `COMPLETED` and `REJECTED`.
266
+ side : str, optional
267
+ One of `BID` and `ASK`.
268
+ start : datetime.datetime, optional
269
+ end : datetime.datetime, optional
270
+ batch : int, optional
271
+ 1-based index of the batch of orders.
272
+ batch_size : int, optional
273
+ The maximum number of orders in one batch.
274
+ newest_first : bool, optional
275
+ Whether to sort orders in descending or ascending order by creation time.
221
276
 
222
277
  Returns
223
278
  -------
224
- list of afp.schemas.Order
279
+ list of afp.schemas.OrderFill
225
280
  """
226
- return self._exchange.get_open_orders(product_id)
281
+ if intent_account_id is None:
282
+ intent_account_id = self._authenticator.address
283
+
284
+ filter = OrderFilter(
285
+ intent_account_id=intent_account_id,
286
+ product_id=product_id,
287
+ type=None if type_ is None else OrderType(type_.upper()),
288
+ states=[OrderState(state.upper()) for state in states],
289
+ side=None if side is None else OrderSide(side.upper()),
290
+ start=start,
291
+ end=end,
292
+ batch=batch,
293
+ batch_size=batch_size,
294
+ newest_first=newest_first,
295
+ )
296
+ return self._exchange.get_orders(filter)
297
+
298
+ @refresh_token_on_expiry
299
+ def open_orders(self, product_id: str | None = None) -> list[Order]:
300
+ """Deprecated alias of Trading.orders(type_="LIMIT_ORDER", states=("OPEN", "PARTIAL"))."""
301
+ warnings.warn(
302
+ "Trading.open_orders() is deprecated. Use "
303
+ 'Trading.orders(type_="LIMIT_ORDER", states=("OPEN", "PARTIAL")) instead.',
304
+ DeprecationWarning,
305
+ stacklevel=2,
306
+ )
307
+ return self.orders(
308
+ type_="limit_order", states=("open", "partial"), product_id=product_id
309
+ )
227
310
 
228
311
  @refresh_token_on_expiry
229
312
  def order_fills(
230
313
  self,
231
314
  *,
232
315
  product_id: str | None = None,
233
- margin_account_id: str | None = None,
316
+ intent_account_id: str | None = None,
234
317
  intent_hash: str | None = None,
235
318
  start: datetime | None = None,
236
319
  end: datetime | None = None,
320
+ trade_states: Iterable[str] = (),
321
+ batch: int = 1,
322
+ batch_size: int = DEFAULT_BATCH_SIZE,
323
+ newest_first: bool = True,
237
324
  ) -> list[OrderFill]:
238
325
  """Retrieves the authenticated account's order fills that match the given
239
326
  parameters.
240
327
 
328
+ If there are more than `batch_size` number of order fills then they can be
329
+ queried in batches.
330
+
241
331
  Parameters
242
332
  ----------
243
333
  product_id : str, optional
244
- margin_account_id : str, optional
334
+ intent_account_id : str, optional
335
+ Defaults to the address of the authenticated account.
245
336
  intent_hash : str, optional
246
337
  start : datetime.datetime, optional
247
338
  end : datetime.datetime, optional
339
+ trade_states : iterable of str
340
+ Any of `PENDING`, `CLEARED` and `REJECTED`.
341
+ batch : int, optional
342
+ 1-based index of the batch of order fills.
343
+ batch_size : int, optional
344
+ The maximum number of order fills in one batch.
345
+ newest_first : bool, optional
346
+ Whether to sort order fills in descending or ascending order by creation time.
248
347
 
249
348
  Returns
250
349
  -------
251
350
  list of afp.schemas.OrderFill
252
351
  """
352
+ if intent_account_id is None:
353
+ intent_account_id = self._authenticator.address
354
+
253
355
  filter = OrderFillFilter(
254
- intent_account_id=self._authenticator.address,
356
+ intent_account_id=intent_account_id,
255
357
  product_id=product_id,
256
- margin_account_id=margin_account_id,
257
358
  intent_hash=intent_hash,
258
359
  start=start,
259
360
  end=end,
260
- trade_state=None,
361
+ trade_states=[TradeState(state.upper()) for state in trade_states],
362
+ batch=batch,
363
+ batch_size=batch_size,
364
+ newest_first=newest_first,
261
365
  )
262
366
  return self._exchange.get_order_fills(filter)
263
367
 
@@ -266,34 +370,49 @@ class Trading(ExchangeAPI):
266
370
  self,
267
371
  *,
268
372
  product_id: str | None = None,
269
- margin_account_id: str | None = None,
373
+ intent_account_id: str | None = None,
270
374
  intent_hash: str | None = None,
375
+ trade_states: Iterable[str] = ("PENDING",),
271
376
  ) -> Generator[OrderFill, None, None]:
272
377
  """Subscribes to the authenticated account's new order fills that match the
273
378
  given parameters.
274
379
 
275
- Returns a generator that yields new order fills as they are published by the
276
- exchange. A new order fill gets publised as soon as there is a match in the
277
- order book, before the trade is submitted to clearing.
380
+ Returns a generator that yields order fills as they are published by the
381
+ exchange.
382
+
383
+ If `trade_states` includes `PENDING` (the default value) then a new order fill
384
+ is yielded as soon as there is a match in the order book, before the trade is
385
+ submitted to clearing.
386
+
387
+ If `trade_states` is empty or more than one trade state is specified then
388
+ updates to order fills are yielded at every state transition.
278
389
 
279
390
  Parameters
280
391
  ----------
281
392
  product_id : str, optional
282
- margin_account_id : str, optional
393
+ intent_account_id : str, optional
394
+ Defaults to the address of the authenticated account.
283
395
  intent_hash : str, optional
396
+ trade_states: iterable of str
397
+ Any of `PENDING`, `CLEARED` and `REJECTED`.
284
398
 
285
399
  Yields
286
400
  -------
287
401
  afp.schemas.OrderFill
288
402
  """
403
+ if intent_account_id is None:
404
+ intent_account_id = self._authenticator.address
405
+
289
406
  filter = OrderFillFilter(
290
- intent_account_id=self._authenticator.address,
407
+ intent_account_id=intent_account_id,
291
408
  product_id=product_id,
292
- margin_account_id=margin_account_id,
293
409
  intent_hash=intent_hash,
294
410
  start=None,
295
411
  end=None,
296
- trade_state=TradeState.PENDING,
412
+ trade_states=[TradeState(state.upper()) for state in trade_states],
413
+ batch=None,
414
+ batch_size=None,
415
+ newest_first=None,
297
416
  )
298
417
  yield from self._exchange.iter_order_fills(filter)
299
418
 
@@ -339,3 +458,71 @@ class Trading(ExchangeAPI):
339
458
  """
340
459
  value = validators.validate_hexstr32(product_id)
341
460
  yield from self._exchange.iter_market_depth_data(value)
461
+
462
+ def ohlcv(
463
+ self,
464
+ product_id: str,
465
+ start: datetime | None = None,
466
+ interval: timedelta = timedelta(minutes=5),
467
+ ) -> list[OHLCVItem]:
468
+ """Retrieves Open-High-Low-Close-Volume time series data for the given product.
469
+
470
+ Parameters
471
+ ----------
472
+ product_id : str
473
+ start : datetime
474
+ Defaults to 1 day ago.
475
+ interval : timedelta
476
+ The distance between 2 data points. Gets rounded to a multiple of 5 seconds.
477
+ Defaults to 5 minutes.
478
+
479
+ Returns
480
+ -------
481
+ list of afp.schemas.OHLCVItem
482
+
483
+ Raises
484
+ ------
485
+ afp.exceptions.NotFoundError
486
+ If no such product exists.
487
+ """
488
+ if start is None:
489
+ start = datetime.now() - timedelta(days=1)
490
+
491
+ product_id = validators.validate_hexstr32(product_id)
492
+ start_timestamp = int(start.timestamp())
493
+ interval_secs = int(validators.validate_timedelta(interval).total_seconds())
494
+ return self._exchange.get_time_series_data(
495
+ product_id, start_timestamp, interval_secs
496
+ )
497
+
498
+ def iter_ohlcv(
499
+ self, product_id: str, interval: timedelta = timedelta(seconds=5)
500
+ ) -> Generator[OHLCVItem, None, None]:
501
+ """Subscribes to Open-High-Low-Close-Volume time series data updates for the
502
+ given product.
503
+
504
+ Returns a generator that yields OHLCV data points as they are published
505
+ by the exhange.
506
+
507
+ Parameters
508
+ ----------
509
+ product_id : str
510
+ interval : timedelta
511
+ The distance between 2 data points. Gets rounded to a multiple of 5 seconds.
512
+ Defaults to 5 seconds.
513
+
514
+ Yields
515
+ -------
516
+ afp.schemas.OHLCVItem
517
+
518
+ Raises
519
+ ------
520
+ afp.exceptions.NotFoundError
521
+ If no such product exists.
522
+ """
523
+ product_id = validators.validate_hexstr32(product_id)
524
+ start_timestamp = int(datetime.now().timestamp())
525
+ interval_secs = int(validators.validate_timedelta(interval).total_seconds())
526
+ yield from self._exchange.iter_time_series_data(
527
+ product_id, start_timestamp, interval_secs
528
+ )
afp/constants.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import os
2
+ from importlib import metadata
2
3
  from types import SimpleNamespace
3
4
 
4
5
 
@@ -6,10 +7,12 @@ def _int_or_none(value: str | None) -> int | None:
6
7
  return int(value) if value is not None else None
7
8
 
8
9
 
9
- USER_AGENT = "afp-sdk"
10
+ # Venue API constants
11
+ USER_AGENT = "afp-sdk/{}".format(metadata.version("afp-sdk"))
12
+ DEFAULT_BATCH_SIZE = 50
10
13
  DEFAULT_EXCHANGE_API_VERSION = 1
11
14
 
12
- # Constants from clearing/contracts/lib/constants.sol
15
+ # Clearing System constants
13
16
  RATE_MULTIPLIER = 10**4
14
17
  FEE_RATE_MULTIPLIER = 10**6
15
18
  FULL_PRECISION_MULTIPLIER = 10**18
afp/enums.py CHANGED
@@ -4,12 +4,14 @@ from enum import StrEnum
4
4
  class ListingState(StrEnum):
5
5
  PRIVATE = "PRIVATE"
6
6
  PUBLIC = "PUBLIC"
7
+ READ_ONLY = "READ_ONLY"
7
8
  DELISTED = "DELISTED"
8
9
 
9
10
 
10
11
  class OrderType(StrEnum):
11
12
  LIMIT_ORDER = "LIMIT_ORDER"
12
13
  CANCEL_ORDER = "CANCEL_ORDER"
14
+ EXCHANGE_INITIATED_CANCELLATION = "EXCHANGE_INITIATED_CANCELLATION"
13
15
 
14
16
 
15
17
  class OrderState(StrEnum):
afp/exchange.py CHANGED
@@ -16,11 +16,14 @@ from .exceptions import (
16
16
  from .schemas import (
17
17
  ExchangeParameters,
18
18
  ExchangeProduct,
19
+ ExchangeProductFilter,
19
20
  ExchangeProductListingSubmission,
20
21
  ExchangeProductUpdateSubmission,
21
22
  LoginSubmission,
22
23
  MarketDepthData,
24
+ OHLCVItem,
23
25
  Order,
26
+ OrderFilter,
24
27
  OrderFill,
25
28
  OrderFillFilter,
26
29
  OrderSubmission,
@@ -51,8 +54,12 @@ class ExchangeClient:
51
54
  return ExchangeParameters(**response.json())
52
55
 
53
56
  # GET /products
54
- def get_approved_products(self) -> list[ExchangeProduct]:
55
- response = self._send_request("GET", "/products")
57
+ def get_approved_products(
58
+ self, filter: ExchangeProductFilter
59
+ ) -> list[ExchangeProduct]:
60
+ response = self._send_request(
61
+ "GET", "/products", params=filter.model_dump(exclude_none=True)
62
+ )
56
63
  return [ExchangeProduct(**item) for item in response.json()["products"]]
57
64
 
58
65
  # GET /products/{product_id}
@@ -86,9 +93,9 @@ class ExchangeClient:
86
93
  return Order(**response.json())
87
94
 
88
95
  # GET /orders
89
- def get_open_orders(self, product_id: str | None = None) -> list[Order]:
96
+ def get_orders(self, filter: OrderFilter) -> list[Order]:
90
97
  response = self._send_request(
91
- "GET", "/orders", params=({"product_id": product_id} if product_id else {})
98
+ "GET", "/orders", params=filter.model_dump(exclude_none=True)
92
99
  )
93
100
  return [Order(**item) for item in response.json()["orders"]]
94
101
 
@@ -132,6 +139,30 @@ class ExchangeClient:
132
139
  for line in response.iter_lines():
133
140
  yield MarketDepthData.model_validate_json(line)
134
141
 
142
+ # GET /time-series/{product_id}
143
+ def get_time_series_data(
144
+ self, product_id: str, start: int, interval: int
145
+ ) -> list[OHLCVItem]:
146
+ response = self._send_request(
147
+ "GET",
148
+ f"/time-series/{product_id}",
149
+ params=dict(start=start, interval=interval),
150
+ )
151
+ return [OHLCVItem(**item) for item in response.json()["data"]]
152
+
153
+ # GET /stream/time-series/{product_id}
154
+ def iter_time_series_data(
155
+ self, product_id: str, start: int, interval: int
156
+ ) -> Generator[OHLCVItem, None, None]:
157
+ response = self._send_request(
158
+ "GET",
159
+ f"/stream/time-series/{product_id}",
160
+ params=dict(start=start, interval=interval),
161
+ stream=True,
162
+ )
163
+ for line in response.iter_lines():
164
+ yield OHLCVItem.model_validate_json(line)
165
+
135
166
  def _send_request(
136
167
  self,
137
168
  method: str,
@@ -167,11 +198,19 @@ class ExchangeClient:
167
198
  raise AuthorizationError(http_error) from http_error
168
199
  if http_error.response.status_code == requests.codes.NOT_FOUND:
169
200
  raise NotFoundError(http_error) from http_error
170
-
171
- try:
172
- reason = response.json()["detail"]
173
- except (json.JSONDecodeError, KeyError):
174
- reason = response.text
175
- raise ValidationError(reason) from http_error
201
+ if http_error.response.status_code == requests.codes.BAD_REQUEST:
202
+ try:
203
+ reason = response.json()["detail"]
204
+ except (json.JSONDecodeError, KeyError):
205
+ reason = http_error
206
+ raise ValidationError(reason) from http_error
207
+ if http_error.response.status_code == requests.codes.UNPROCESSABLE:
208
+ try:
209
+ reason = ", ".join(err["msg"] for err in response.json()["detail"])
210
+ except (json.JSONDecodeError, KeyError, TypeError):
211
+ reason = http_error
212
+ raise ValidationError(reason) from http_error
213
+
214
+ raise ExchangeError(http_error) from http_error
176
215
 
177
216
  return response
afp/schemas.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime
2
2
  from decimal import Decimal
3
3
  from functools import partial
4
- from typing import Annotated, Any
4
+ from typing import Annotated, Any, Literal
5
5
 
6
6
  import inflection
7
7
  from pydantic import (
@@ -12,6 +12,7 @@ from pydantic import (
12
12
  ConfigDict,
13
13
  Field,
14
14
  PlainSerializer,
15
+ computed_field,
15
16
  )
16
17
 
17
18
  from . import validators
@@ -40,6 +41,33 @@ class Model(BaseModel):
40
41
  return super().model_dump_json(by_alias=by_alias, **kwargs)
41
42
 
42
43
 
44
+ class PaginationFilter(Model):
45
+ batch: Annotated[None | int, Field(gt=0, exclude=True)]
46
+ batch_size: Annotated[None | int, Field(gt=0, exclude=True)]
47
+ newest_first: Annotated[None | bool, Field(exclude=True)]
48
+
49
+ @computed_field
50
+ @property
51
+ def page(self) -> None | int:
52
+ return self.batch
53
+
54
+ @computed_field
55
+ @property
56
+ def page_size(self) -> None | int:
57
+ return self.batch_size
58
+
59
+ @computed_field
60
+ @property
61
+ def sort(self) -> None | Literal["ASC", "DESC"]:
62
+ match self.newest_first:
63
+ case None:
64
+ return None
65
+ case True:
66
+ return "DESC"
67
+ case False:
68
+ return "ASC"
69
+
70
+
43
71
  # Authentication
44
72
 
45
73
 
@@ -78,6 +106,10 @@ class ExchangeProduct(Model):
78
106
  return self.id
79
107
 
80
108
 
109
+ class ExchangeProductFilter(PaginationFilter):
110
+ pass
111
+
112
+
81
113
  class IntentData(Model):
82
114
  trading_protocol_id: str
83
115
  product_id: str
@@ -106,6 +138,21 @@ class Order(Model):
106
138
  intent: Intent
107
139
 
108
140
 
141
+ class OrderFilter(PaginationFilter):
142
+ intent_account_id: str
143
+ product_id: None | Annotated[str, AfterValidator(validators.validate_hexstr32)]
144
+ type: None | OrderType
145
+ states: Annotated[list[OrderState], Field(exclude=True)]
146
+ side: None | OrderSide
147
+ start: None | Timestamp
148
+ end: None | Timestamp
149
+
150
+ @computed_field
151
+ @property
152
+ def state(self) -> str | None:
153
+ return ",".join(self.states) if self.states else None
154
+
155
+
109
156
  class OrderCancellationData(Model):
110
157
  intent_hash: Annotated[str, AfterValidator(validators.validate_hexstr32)]
111
158
  nonce: int
@@ -137,16 +184,18 @@ class OrderFill(Model):
137
184
  price: Decimal
138
185
 
139
186
 
140
- class OrderFillFilter(Model):
187
+ class OrderFillFilter(PaginationFilter):
141
188
  intent_account_id: str
142
189
  product_id: None | Annotated[str, AfterValidator(validators.validate_hexstr32)]
143
- margin_account_id: (
144
- None | Annotated[str, AfterValidator(validators.validate_address)]
145
- )
146
190
  intent_hash: None | Annotated[str, AfterValidator(validators.validate_hexstr32)]
147
191
  start: None | Timestamp
148
192
  end: None | Timestamp
149
- trade_state: None | TradeState
193
+ trade_states: Annotated[list[TradeState], Field(exclude=True)]
194
+
195
+ @computed_field
196
+ @property
197
+ def trade_state(self) -> str | None:
198
+ return ",".join(self.trade_states) if self.trade_states else None
150
199
 
151
200
 
152
201
  class MarketDepthItem(Model):
@@ -160,6 +209,15 @@ class MarketDepthData(Model):
160
209
  asks: list[MarketDepthItem]
161
210
 
162
211
 
212
+ class OHLCVItem(Model):
213
+ timestamp: Timestamp
214
+ open: Decimal
215
+ high: Decimal
216
+ low: Decimal
217
+ close: Decimal
218
+ volume: int
219
+
220
+
163
221
  # Clearing API
164
222
 
165
223
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: afp-sdk
3
- Version: 0.5.1
3
+ Version: 0.5.3
4
4
  Summary: Autonomous Futures Protocol Python SDK
5
5
  Keywords: autonity,web3,trading,crypto,prediction,forecast,markets
6
6
  License-Expression: MIT
@@ -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=exQQ3LJA6XCbRAUBuYVjSJ5PHFi4COyqDORxC37LLfQ,10379
8
+ afp/api/trading.py,sha256=y5Q2KApEpUau9R8G9KUqOdm4ymhw9Hbu9d2dkG80AsQ,17262
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
@@ -22,16 +22,16 @@ afp/bindings/product_registry.py,sha256=-_h786jzMCsaTqqnoxpmVgBkGf45eCUMthp_Pkqr
22
22
  afp/bindings/system_viewer.py,sha256=0FivdhpfXMrBesXcHkfO9uELyr7GiRmGe36xS5sURGE,41094
23
23
  afp/bindings/trading_protocol.py,sha256=ZloF3REbjFq9v0UGVsM0_Lk0EhfWJKdeJ0PzVEnyZo0,39573
24
24
  afp/config.py,sha256=_WKywiuty8poE1A0v46uBe1JGpfCzRlxCPamKennfpE,699
25
- afp/constants.py,sha256=XIph4R0Tx-BPw_NZJgWUtb7NdS9sYzEciFRSStm3VMs,1840
25
+ afp/constants.py,sha256=EvDhLpKBOsc8OHGm1paiUAdAetPGD4nyi13coB8rd14,1930
26
26
  afp/decorators.py,sha256=SEUQtbgPGc4iVPtBQV2eiCejcDAVImmXcI0uPXFhtJA,2774
27
- afp/enums.py,sha256=U5b_3RteMiVJozp4WYAKzUfPXI1RdJLEK19jAg-LN8Q,588
27
+ afp/enums.py,sha256=9JhwdLcTNhyKabKdrALAlCeL3C5XfeXYSSSXSCsuTzE,688
28
28
  afp/exceptions.py,sha256=frdS-EH84K0fOf92RgRgNkTe3VII2m36XNCS8eyXLLM,390
29
- afp/exchange.py,sha256=MXQ-RhcI76l6bA9_IDTUq0RQGfwxBisrRhl6eC_yH4I,5983
29
+ afp/exchange.py,sha256=QQAfglOzsxo_T0BUOFAkxI2wUbda1sKStMXUO9IcDbw,7498
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=M6qr0GhCeT7KevzOso5dIhydYzaYx78Rs66IWdadJO8,5284
32
+ afp/schemas.py,sha256=zyZOPvAdDnXp60kqYqX-OoWnKvRLPZT7at0A0MHlurc,6746
33
33
  afp/validators.py,sha256=zQvPu3HDu6ADnEE72EJlS5hc1-xfru78Mzd74s1u_EM,1841
34
- afp_sdk-0.5.1.dist-info/licenses/LICENSE,sha256=ZdaKItgc2ppfqta2OJV0oHpSJiK87PUxmUkUo-_0SB8,1065
35
- afp_sdk-0.5.1.dist-info/WHEEL,sha256=lh7MMMfiuFQLQaR9J7pNBODdWf-aa5UOeuuDAol3xps,79
36
- afp_sdk-0.5.1.dist-info/METADATA,sha256=RM6EyQq2W_ZlrkRtYcGf85pZg7S15poSNJq7CUEHz0w,6354
37
- afp_sdk-0.5.1.dist-info/RECORD,,
34
+ afp_sdk-0.5.3.dist-info/licenses/LICENSE,sha256=ZdaKItgc2ppfqta2OJV0oHpSJiK87PUxmUkUo-_0SB8,1065
35
+ afp_sdk-0.5.3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
36
+ afp_sdk-0.5.3.dist-info/METADATA,sha256=__Jo9oroNSkI1O9-vAOSA2HrSEONWX95AB4nLKkrjg0,6354
37
+ afp_sdk-0.5.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.8.20
2
+ Generator: uv 0.8.24
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any