afp-sdk 0.1.2__tar.gz → 0.2.0__tar.gz

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.
Files changed (50) hide show
  1. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/CHANGELOG.md +15 -0
  2. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/PKG-INFO +1 -1
  3. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/api/trading.py +13 -1
  4. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/trading_protocol.py +121 -0
  5. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/validators.py +13 -0
  6. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/pyproject.toml +1 -1
  7. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/tests/test_validators.py +24 -0
  8. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/uv.lock +2 -2
  9. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/.env.template +0 -0
  10. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/.envrc +0 -0
  11. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/LICENSE +0 -0
  12. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/README.md +0 -0
  13. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/__init__.py +0 -0
  14. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/api/__init__.py +0 -0
  15. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/api/admin.py +0 -0
  16. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/api/base.py +0 -0
  17. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/api/builder.py +0 -0
  18. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/api/clearing.py +0 -0
  19. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/api/liquidation.py +0 -0
  20. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/__init__.py +0 -0
  21. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/auctioneer_facet.py +0 -0
  22. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/bankruptcy_facet.py +0 -0
  23. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/clearing_facet.py +0 -0
  24. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/erc20.py +0 -0
  25. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/facade.py +0 -0
  26. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/final_settlement_facet.py +0 -0
  27. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/margin_account.py +0 -0
  28. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/margin_account_registry.py +0 -0
  29. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/mark_price_tracker_facet.py +0 -0
  30. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/oracle_provider.py +0 -0
  31. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/bindings/product_registry.py +0 -0
  32. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/config.py +0 -0
  33. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/decorators.py +0 -0
  34. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/enums.py +0 -0
  35. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/exceptions.py +0 -0
  36. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/exchange.py +0 -0
  37. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/py.typed +0 -0
  38. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/schemas.py +0 -0
  39. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/afp/signing.py +0 -0
  40. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/devenv.lock +0 -0
  41. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/devenv.nix +0 -0
  42. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/devenv.yaml +0 -0
  43. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/examples/cancel_order.py +0 -0
  44. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/examples/create_product.py +0 -0
  45. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/examples/execute_trade.py +0 -0
  46. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/tests/__init__.py +0 -0
  47. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/tests/test_decorators.py +0 -0
  48. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/tests/test_hashing.py +0 -0
  49. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/tests/test_login.py +0 -0
  50. {afp_sdk-0.1.2 → afp_sdk-0.2.0}/tests/test_schemas.py +0 -0
@@ -1,3 +1,17 @@
1
+ ## [v0.2.0] - 2025-09-02
2
+
3
+ ### Changed
4
+
5
+ - Validate limit price to make sure it conforms to the product's tick size
6
+ ([#12](https://github.com/autonity/afp-sdk/pull/12))
7
+ - Update Clearing System contract bindings for Autonity Bakerloo (Nile) Testnet
8
+ deployment as of 2025-09-02 ([#13](https://github.com/autonity/afp-sdk/pull/13))
9
+
10
+ ### Added
11
+
12
+ - Add optional `rounding` argument to `afp.Trading.create_intent`
13
+ ([#12](https://github.com/autonity/afp-sdk/pull/12))
14
+
1
15
  ## [v0.1.2] - 2025-08-28
2
16
 
3
17
  ### Fixed
@@ -14,6 +28,7 @@
14
28
 
15
29
  _First release._
16
30
 
31
+ [v0.2.0]: https://github.com/autonity/afp-sdk/releases/tag/v0.2.0
17
32
  [v0.1.2]: https://github.com/autonity/afp-sdk/releases/tag/v0.1.2
18
33
  [v0.1.1]: https://github.com/autonity/afp-sdk/releases/tag/v0.1.1
19
34
  [v0.1.0]: https://github.com/autonity/afp-sdk/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: afp-sdk
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: Autonomous Futures Protocol Python SDK
5
5
  Keywords: autonity,web3,trading,crypto,prediction,forecast,markets
6
6
  License-Expression: MIT
@@ -54,6 +54,7 @@ class Trading(ExchangeAPI):
54
54
  max_trading_fee_rate: Decimal,
55
55
  good_until_time: datetime,
56
56
  margin_account_id: str | None = None,
57
+ rounding: str | None = None,
57
58
  ) -> Intent:
58
59
  """Creates an intent with the given intent data, generates its hash and signs it
59
60
  with the configured account's private key.
@@ -62,6 +63,13 @@ class Trading(ExchangeAPI):
62
63
  is assumed to be the same as the margin account if the margin account ID is not
63
64
  specified.
64
65
 
66
+ The limit price must have at most as many fractional digits as the product's
67
+ tick size, or if `rounding` is specified then the limit price is rounded to the
68
+ appropriate number of fractional digits. `rounding` may be one of
69
+ `ROUND_CEILING`, `ROUND_FLOOR`, `ROUND_UP`, `ROUND_DOWN`, `ROUND_HALF_UP`,
70
+ `ROUND_HALF_DOWN`, `ROUND_HALF_EVEN` and `ROUND_05UP`; see the rounding modes of
71
+ the `decimal` module of the Python Standard Library.
72
+
65
73
  Parameters
66
74
  ----------
67
75
  product : afp.schemas.ExchangeProduct
@@ -71,6 +79,8 @@ class Trading(ExchangeAPI):
71
79
  max_trading_fee_rate : decimal.Decimal
72
80
  good_until_time : datetime.datetime
73
81
  margin_account_id : str, optional
82
+ rounding : str, optional
83
+ A rounding mode of the `decimal` module or `None` for no rounding.
74
84
 
75
85
  Returns
76
86
  -------
@@ -85,7 +95,9 @@ class Trading(ExchangeAPI):
85
95
  intent_data = IntentData(
86
96
  trading_protocol_id=self._trading_protocol_id,
87
97
  product_id=product.id,
88
- limit_price=limit_price,
98
+ limit_price=validators.validate_limit_price(
99
+ Decimal(limit_price), product.tick_size, rounding
100
+ ),
89
101
  quantity=quantity,
90
102
  max_trading_fee_rate=max_trading_fee_rate,
91
103
  side=getattr(OrderSide, side.upper()),
@@ -59,6 +59,24 @@ class Trade:
59
59
  intents: typing.List[Intent]
60
60
 
61
61
 
62
+ @dataclass
63
+ class Settlement:
64
+ """Port of `struct Settlement` on the IMarginAccount contract."""
65
+
66
+ position_id: hexbytes.HexBytes
67
+ quantity: int
68
+ price: int
69
+
70
+
71
+ @dataclass
72
+ class Order:
73
+ """Port of `struct Order` on the ITradingProtocol contract."""
74
+
75
+ margin_account_id: eth_typing.ChecksumAddress
76
+ margin_account_contract: eth_typing.ChecksumAddress
77
+ settlement: Settlement
78
+
79
+
62
80
  class TradingProtocol:
63
81
  """TradingProtocol contract binding.
64
82
 
@@ -101,6 +119,11 @@ class TradingProtocol:
101
119
  """Binding for `event RoleRevoked` on the TradingProtocol contract."""
102
120
  return self._contract.events.RoleRevoked
103
121
 
122
+ @property
123
+ def SequenceExecuted(self) -> contract.ContractEvent:
124
+ """Binding for `event SequenceExecuted` on the TradingProtocol contract."""
125
+ return self._contract.events.SequenceExecuted
126
+
104
127
  @property
105
128
  def Upgraded(self) -> contract.ContractEvent:
106
129
  """Binding for `event Upgraded` on the TradingProtocol contract."""
@@ -450,6 +473,36 @@ class TradingProtocol:
450
473
  clearing_address,
451
474
  )
452
475
 
476
+ def order_mae_check(
477
+ self,
478
+ orders: typing.List[Order],
479
+ ) -> typing.List[bool]:
480
+ """Binding for `orderMAECheck` on the TradingProtocol contract.
481
+
482
+ Parameters
483
+ ----------
484
+ orders : typing.List[Order]
485
+
486
+ Returns
487
+ -------
488
+ typing.List[bool]
489
+ """
490
+ return_value = self._contract.functions.orderMAECheck(
491
+ [
492
+ (
493
+ item.margin_account_id,
494
+ item.margin_account_contract,
495
+ (
496
+ item.settlement.position_id,
497
+ item.settlement.quantity,
498
+ item.settlement.price,
499
+ ),
500
+ )
501
+ for item in orders
502
+ ],
503
+ ).call()
504
+ return [bool(return_value_elem) for return_value_elem in return_value]
505
+
453
506
  def proxiable_uuid(
454
507
  self,
455
508
  ) -> hexbytes.HexBytes:
@@ -718,6 +771,25 @@ ABI = typing.cast(
718
771
  "name": "RoleRevoked",
719
772
  "type": "event",
720
773
  },
774
+ {
775
+ "anonymous": False,
776
+ "inputs": [
777
+ {
778
+ "indexed": False,
779
+ "internalType": "bool[]",
780
+ "name": "results",
781
+ "type": "bool[]",
782
+ },
783
+ {
784
+ "indexed": False,
785
+ "internalType": "bytes[]",
786
+ "name": "errors",
787
+ "type": "bytes[]",
788
+ },
789
+ ],
790
+ "name": "SequenceExecuted",
791
+ "type": "event",
792
+ },
721
793
  {
722
794
  "anonymous": False,
723
795
  "inputs": [
@@ -1100,6 +1172,55 @@ ABI = typing.cast(
1100
1172
  "stateMutability": "nonpayable",
1101
1173
  "type": "function",
1102
1174
  },
1175
+ {
1176
+ "inputs": [
1177
+ {
1178
+ "components": [
1179
+ {
1180
+ "internalType": "address",
1181
+ "name": "marginAccountID",
1182
+ "type": "address",
1183
+ },
1184
+ {
1185
+ "internalType": "address",
1186
+ "name": "marginAccountContract",
1187
+ "type": "address",
1188
+ },
1189
+ {
1190
+ "components": [
1191
+ {
1192
+ "internalType": "bytes32",
1193
+ "name": "positionId",
1194
+ "type": "bytes32",
1195
+ },
1196
+ {
1197
+ "internalType": "int256",
1198
+ "name": "quantity",
1199
+ "type": "int256",
1200
+ },
1201
+ {
1202
+ "internalType": "uint256",
1203
+ "name": "price",
1204
+ "type": "uint256",
1205
+ },
1206
+ ],
1207
+ "internalType": "struct IMarginAccount.Settlement",
1208
+ "name": "settlement",
1209
+ "type": "tuple",
1210
+ },
1211
+ ],
1212
+ "internalType": "struct ITradingProtocol.Order[]",
1213
+ "name": "orders",
1214
+ "type": "tuple[]",
1215
+ }
1216
+ ],
1217
+ "name": "orderMAECheck",
1218
+ "outputs": [
1219
+ {"internalType": "bool[]", "name": "results", "type": "bool[]"}
1220
+ ],
1221
+ "stateMutability": "view",
1222
+ "type": "function",
1223
+ },
1103
1224
  {
1104
1225
  "inputs": [],
1105
1226
  "name": "proxiableUUID",
@@ -1,4 +1,5 @@
1
1
  from datetime import datetime, timedelta
2
+ from decimal import Decimal
2
3
 
3
4
  from binascii import Error
4
5
  from eth_typing.evm import ChecksumAddress
@@ -41,3 +42,15 @@ def validate_address(value: str) -> ChecksumAddress:
41
42
  return Web3.to_checksum_address(value)
42
43
  except ValueError:
43
44
  raise ValueError(f"{value} is not a valid blockchain address")
45
+
46
+
47
+ def validate_limit_price(
48
+ value: Decimal, tick_size: int, rounding: str | None = None
49
+ ) -> Decimal:
50
+ if rounding is None:
51
+ num_fractional_digits = abs(int(value.normalize().as_tuple().exponent))
52
+ if num_fractional_digits > tick_size:
53
+ raise ValueError(
54
+ f"Limit price {value} can have at most {tick_size} fractional digits"
55
+ )
56
+ return value.quantize(Decimal("10") ** -tick_size, rounding=rounding)
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "afp-sdk"
7
- version = "0.1.2"
7
+ version = "0.2.0"
8
8
  description = "Autonomous Futures Protocol Python SDK"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,4 +1,6 @@
1
+ import decimal
1
2
  from datetime import datetime, UTC
3
+ from decimal import Decimal
2
4
 
3
5
  import pytest
4
6
 
@@ -45,3 +47,25 @@ def test_validate_hexstr32__pass():
45
47
  def test_validate_hexstr32__error(value):
46
48
  with pytest.raises(ValueError):
47
49
  validators.validate_hexstr32(value)
50
+
51
+
52
+ def test_validate_limit_price__pass():
53
+ assert str(validators.validate_limit_price(Decimal("1.25"), 2)) == "1.25"
54
+ assert str(validators.validate_limit_price(Decimal("1.25"), 4)) == "1.2500"
55
+
56
+
57
+ def test_validate_limit_price__rounding():
58
+ assert (
59
+ str(validators.validate_limit_price(Decimal("1.25"), 1, decimal.ROUND_DOWN))
60
+ == "1.2"
61
+ )
62
+
63
+
64
+ def test_validate_limit_price__error():
65
+ with pytest.raises(ValueError):
66
+ validators.validate_limit_price(Decimal("1.25"), 1)
67
+
68
+
69
+ def test_validate_limit_price__invalid_rounding_mode():
70
+ with pytest.raises(TypeError):
71
+ validators.validate_limit_price(Decimal("1.25"), 1, "foobar")
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 3
2
+ revision = 2
3
3
  requires-python = ">=3.11"
4
4
 
5
5
  [[package]]
@@ -13,7 +13,7 @@ wheels = [
13
13
 
14
14
  [[package]]
15
15
  name = "afp-sdk"
16
- version = "0.1.2"
16
+ version = "0.2.0"
17
17
  source = { editable = "." }
18
18
  dependencies = [
19
19
  { name = "decorator" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes