tpluspy 0.3.2__tar.gz → 0.5.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.
- {tpluspy-0.3.2 → tpluspy-0.5.0}/PKG-INFO +95 -50
- {tpluspy-0.3.2 → tpluspy-0.5.0}/README.md +84 -47
- {tpluspy-0.3.2 → tpluspy-0.5.0}/setup.py +16 -4
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/_context.py +1 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/markets.py +71 -18
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/orders.py +1 -1
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/settle.py +1 -1
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/vaults.py +77 -1
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/withdraw.py +1 -1
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/api.py +9 -6
- tpluspy-0.5.0/tplus/client/auth.py +455 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/base.py +151 -10
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/clearingengine/__init__.py +2 -2
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/clearingengine/admin.py +59 -24
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/clearingengine/cross_venue.py +3 -2
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/market_data.py +149 -30
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/oms/assetregistry.py +15 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/oms/oms_admin.py +6 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/orderbook.py +411 -100
- tpluspy-0.5.0/tplus/client/websocket.py +58 -0
- tpluspy-0.5.0/tplus/evm/__init__.py +19 -0
- tpluspy-0.5.0/tplus/evm/_manifest.py +45 -0
- tpluspy-0.5.0/tplus/evm/abi.py +117 -0
- tpluspy-0.5.0/tplus/evm/backends/__init__.py +125 -0
- tpluspy-0.5.0/tplus/evm/backends/_ape_project.py +78 -0
- tpluspy-0.5.0/tplus/evm/backends/ape.py +224 -0
- tpluspy-0.5.0/tplus/evm/backends/base.py +196 -0
- tpluspy-0.5.0/tplus/evm/backends/web3.py +476 -0
- tpluspy-0.5.0/tplus/evm/constants.py +2 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/contracts.py +217 -249
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/dev/env.py +23 -20
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/exceptions.py +20 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/managers/chaindata.py +9 -4
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/managers/credential_manager.py +31 -22
- tpluspy-0.5.0/tplus/evm/managers/deposit.py +60 -0
- tpluspy-0.5.0/tplus/evm/managers/evm.py +103 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/managers/registry.py +13 -12
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/managers/settle.py +82 -32
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/managers/vault.py +21 -31
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/managers/withdraw.py +29 -27
- tpluspy-0.5.0/tplus/evm/manifests/tplus-contracts.json +1 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/exceptions.py +8 -1
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/asset_identifier.py +5 -1
- tpluspy-0.5.0/tplus/model/auth.py +19 -0
- tpluspy-0.5.0/tplus/model/batch_order.py +63 -0
- tpluspy-0.5.0/tplus/model/control.py +49 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/klines.py +3 -1
- tpluspy-0.5.0/tplus/model/market.py +93 -0
- tpluspy-0.5.0/tplus/model/open_interest.py +27 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/order.py +62 -1
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/position.py +10 -0
- tpluspy-0.5.0/tplus/model/position_basis.py +26 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/settlement.py +32 -15
- tpluspy-0.5.0/tplus/model/sub_account.py +16 -0
- tpluspy-0.5.0/tplus/model/ticker.py +51 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/trades.py +33 -1
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/user_event.py +1 -1
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/user_margin.py +38 -2
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/withdrawal.py +16 -12
- tpluspy-0.5.0/tplus/types.py +9 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/limit_order.py +7 -2
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/market_order.py +7 -2
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/replace_order.py +18 -4
- tpluspy-0.5.0/tplus/utils/serializers.py +39 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/signing.py +7 -2
- tpluspy-0.5.0/tplus/utils/user/__init__.py +128 -0
- tpluspy-0.5.0/tplus/utils/user/evm.py +145 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/user/manager.py +80 -2
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/user/model.py +183 -9
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tpluspy.egg-info/PKG-INFO +95 -50
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tpluspy.egg-info/SOURCES.txt +15 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tpluspy.egg-info/requires.txt +11 -2
- tpluspy-0.3.2/tplus/client/auth.py +0 -296
- tpluspy-0.3.2/tplus/evm/__init__.py +0 -1
- tpluspy-0.3.2/tplus/evm/abi.py +0 -21
- tpluspy-0.3.2/tplus/evm/constants.py +0 -2
- tpluspy-0.3.2/tplus/evm/managers/deposit.py +0 -46
- tpluspy-0.3.2/tplus/evm/managers/evm.py +0 -12
- tpluspy-0.3.2/tplus/model/batch_order.py +0 -31
- tpluspy-0.3.2/tplus/model/market.py +0 -17
- tpluspy-0.3.2/tplus/types.py +0 -4
- tpluspy-0.3.2/tplus/utils/serializers.py +0 -15
- tpluspy-0.3.2/tplus/utils/user/__init__.py +0 -32
- {tpluspy-0.3.2 → tpluspy-0.5.0}/pyproject.toml +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/setup.cfg +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/__main__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/accounts.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/assets.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/balance.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/debug.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/decimals.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/deposit.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/env.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/params.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/sign.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/stream.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/trades.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/_cli/withdrawal.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/asset_metadata.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/cli_tools/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/cli_tools/options.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/cli_tools/output.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/blockchain.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/clearingengine/admin_settlement.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/clearingengine/assetregistry.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/clearingengine/base.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/clearingengine/decimal.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/clearingengine/vault.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/oms/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/client/withdrawal.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/constants.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/address.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/dev/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/dev/contracts.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/dev/token.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/managers/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/evm/utils.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/logger.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/managers/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/managers/base.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/approval.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/cancel_order.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/chain_address.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/close_all_positions_preview.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/config.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/interest_rates.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/limit_order.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/market_order.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/multisig.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/order_id.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/order_trigger.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/orderbook.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/pagination.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/replace_order.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/risk_parameters.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/signed_message.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/types.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/user_inventory.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/user_simulated_margin.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/model/user_solvency.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/py.typed +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/__init__.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/address.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/amount.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/bytes32.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/decimals.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/domain.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/hex.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/operator.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/timeout.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/user/decrypt.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/user/ed_keyfile.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/user/token_cache.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tplus/utils/user/validate.py +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tpluspy.egg-info/dependency_links.txt +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tpluspy.egg-info/entry_points.txt +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tpluspy.egg-info/not-zip-safe +0 -0
- {tpluspy-0.3.2 → tpluspy-0.5.0}/tpluspy.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tpluspy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: tpluspy: Client utilities for interacting with tplus
|
|
5
5
|
Home-page: https://github.com/tpluslabs/tpluspy
|
|
6
6
|
Author: TPlus Labs
|
|
@@ -29,6 +29,7 @@ Requires-Dist: pycryptodome>=3.17.1
|
|
|
29
29
|
Requires-Dist: pydantic<3,>=2.10.4
|
|
30
30
|
Requires-Dist: websockets<14,>=13.1
|
|
31
31
|
Provides-Extra: test
|
|
32
|
+
Requires-Dist: eth-account<0.14,>=0.13.0; extra == "test"
|
|
32
33
|
Requires-Dist: pytest>=8.0; extra == "test"
|
|
33
34
|
Requires-Dist: pytest-timeout<3,>=2.2.0; extra == "test"
|
|
34
35
|
Requires-Dist: pytest-mock; extra == "test"
|
|
@@ -36,6 +37,7 @@ Requires-Dist: pytest-asyncio; extra == "test"
|
|
|
36
37
|
Requires-Dist: anyio>=4; extra == "test"
|
|
37
38
|
Requires-Dist: pytest-anyio>=0.0.0; extra == "test"
|
|
38
39
|
Requires-Dist: trio>=0.24; extra == "test"
|
|
40
|
+
Requires-Dist: eth-tester[py-evm]>=0.12.0b1; extra == "test"
|
|
39
41
|
Provides-Extra: lint
|
|
40
42
|
Requires-Dist: ruff<0.16,>=0.11.7; extra == "lint"
|
|
41
43
|
Requires-Dist: mypy<2,>=1.18.2; extra == "lint"
|
|
@@ -44,11 +46,17 @@ Requires-Dist: setuptools>=75.6.0; extra == "release"
|
|
|
44
46
|
Requires-Dist: wheel; extra == "release"
|
|
45
47
|
Requires-Dist: twine; extra == "release"
|
|
46
48
|
Provides-Extra: evm
|
|
47
|
-
Requires-Dist: ape-tokens; extra == "evm"
|
|
48
49
|
Requires-Dist: click; extra == "evm"
|
|
49
50
|
Requires-Dist: eip712>=0.3.1; extra == "evm"
|
|
50
|
-
Requires-Dist: eth-
|
|
51
|
+
Requires-Dist: eth-account<0.14,>=0.13.0; extra == "evm"
|
|
52
|
+
Requires-Dist: eth-keys<0.8,>=0.6; extra == "evm"
|
|
51
53
|
Requires-Dist: hexbytes<2,>=1.3.1; extra == "evm"
|
|
54
|
+
Requires-Dist: pyyaml<7,>=6; extra == "evm"
|
|
55
|
+
Requires-Dist: web3<8,>=7.0; extra == "evm"
|
|
56
|
+
Provides-Extra: evm-ape
|
|
57
|
+
Requires-Dist: tpluspy[evm]; extra == "evm-ape"
|
|
58
|
+
Requires-Dist: ape-tokens; extra == "evm-ape"
|
|
59
|
+
Requires-Dist: eth-ape<0.9,>=0.8.49; extra == "evm-ape"
|
|
52
60
|
Provides-Extra: docs
|
|
53
61
|
Requires-Dist: Sphinx<9,>=7.0; extra == "docs"
|
|
54
62
|
Requires-Dist: myst-parser<5,>=2.0; extra == "docs"
|
|
@@ -142,6 +150,41 @@ async def run_client():
|
|
|
142
150
|
asyncio.run(run_client())
|
|
143
151
|
```
|
|
144
152
|
|
|
153
|
+
#### Use EVM accounts as T+ accounts
|
|
154
|
+
|
|
155
|
+
An EVM account can back a T+ account: it signs one fixed message and the T+ identity is derived from that signature, so the same wallet always maps to the same T+ account (the one the T+ frontend derives on wallet login).
|
|
156
|
+
|
|
157
|
+
Pass the account itself anywhere a `User` is accepted, whether that is `default_user=`, a per-call `user=`, or a `signer=`:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from ape import accounts
|
|
161
|
+
|
|
162
|
+
account = accounts.load("me")
|
|
163
|
+
|
|
164
|
+
async with OrderBookClient(API_BASE_URL, default_user=account) as client:
|
|
165
|
+
await client.get_user_inventory() # signs as the T+ user behind the account
|
|
166
|
+
await client.create_limit_order(..., user=account) # per-call override
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
An `eth_account` signer works the same way, with no `[evm]` extra:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from eth_account import Account
|
|
173
|
+
|
|
174
|
+
client = OrderBookClient(API_BASE_URL, default_user=Account.from_key("0x..."))
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Use `load_user_from_ape_account` to hold the `User` itself, for example to read its public key or to pick a sub-account. `load_user` loads stored keyfiles by name, so Ape aliases and keyfile names occupy separate namespaces and cannot collide:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from tplus.utils.user import load_user_from_ape_account
|
|
181
|
+
|
|
182
|
+
user = load_user_from_ape_account("me") # by Ape alias (needs the `evm` extra)
|
|
183
|
+
user = load_user_from_ape_account(account) # an already-loaded Ape account
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The result is a normal T+ `User` and signs every request the same way.
|
|
187
|
+
|
|
145
188
|
#### REST API Usage
|
|
146
189
|
|
|
147
190
|
The client offers async methods for common REST endpoints:
|
|
@@ -225,13 +268,28 @@ print(f"Cancel Order Response: {cancel_response}")
|
|
|
225
268
|
# Replace an Order
|
|
226
269
|
# Original Order ID should be from an existing, open order.
|
|
227
270
|
original_order_id_to_replace = "actual-original-order-id" # Replace with a real order ID
|
|
228
|
-
replace_response = await client.replace_order(
|
|
271
|
+
replace_response, revision = await client.replace_order(
|
|
229
272
|
original_order_id=original_order_id_to_replace,
|
|
230
273
|
asset_id=example_asset,
|
|
231
|
-
new_quantity=6, #
|
|
232
|
-
new_price=1050, #
|
|
274
|
+
new_quantity=6, # Effective lifetime-total quantity
|
|
275
|
+
new_price=1050, # Effective limit price
|
|
233
276
|
)
|
|
234
277
|
print(f"Replace Order Response: {replace_response}")
|
|
278
|
+
# `revision` is the replacement's timestamp_ns; a later amend_order must quote it as
|
|
279
|
+
# expected_authorization_revision.
|
|
280
|
+
|
|
281
|
+
# Replace several open orders in one request (up to 50). Each item is signed on its own;
|
|
282
|
+
# the OMS sends one message per market and returns one status per item, in order.
|
|
283
|
+
batch_replace_response = await client.replace_multiple_orders(
|
|
284
|
+
[
|
|
285
|
+
await client.prepare_replace_order_request(
|
|
286
|
+
original_order_id=oid, asset_id=example_asset, new_quantity=6, new_price=price
|
|
287
|
+
)
|
|
288
|
+
for oid, price in [("order-a", 1050), ("order-b", 1040)]
|
|
289
|
+
]
|
|
290
|
+
)
|
|
291
|
+
for status in batch_replace_response.batch_order_status:
|
|
292
|
+
print(f"{status.order_id}: {status.status} {status.reason or ''}")
|
|
235
293
|
```
|
|
236
294
|
|
|
237
295
|
See `examples/rest_usage.py` for a runnable demonstration.
|
|
@@ -269,68 +327,55 @@ async for trade in client.stream_finalized_trades():
|
|
|
269
327
|
|
|
270
328
|
See `examples/websocket_usage.py` for a runnable demonstration using `asyncio.gather` to run multiple streams concurrently.
|
|
271
329
|
|
|
330
|
+
#### Token Recovery
|
|
331
|
+
|
|
332
|
+
When a bearer token stops working before it actually expires (e.g. from a service restart), both transports recover on their own. A REST response or WebSocket handshake rejected with `401` or `403` re-authenticates and retries once; a second rejection is raised to the caller. Requests rejected concurrently share a single re-authentication.
|
|
333
|
+
|
|
272
334
|
### Contracts
|
|
273
335
|
|
|
274
|
-
To interact with the contracts or sign T+ settlement messages,
|
|
336
|
+
To interact with the on-chain T+ contracts (or sign T+ settlement messages),
|
|
337
|
+
install one of the EVM extras:
|
|
275
338
|
|
|
276
339
|
```shell
|
|
277
|
-
pip install tpluspy[evm]
|
|
340
|
+
pip install "tpluspy[evm]" # web3.py backend (no Ape)
|
|
341
|
+
pip install "tpluspy[evm-ape]" # adds the Ape backend on top of [evm]
|
|
278
342
|
```
|
|
279
343
|
|
|
280
|
-
|
|
281
|
-
|
|
344
|
+
The Python API is identical either way. With the **web3.py** backend, point it
|
|
345
|
+
at a node via `WEB3_PROVIDER_URI` (read by `web3.auto`) — or call
|
|
346
|
+
`tplus.evm.use_web3(rpc_url=...)`:
|
|
282
347
|
|
|
283
348
|
```shell
|
|
284
|
-
|
|
349
|
+
export WEB3_PROVIDER_URI="https://arb1.arbitrum.io/rpc"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
```python
|
|
353
|
+
from tplus.evm.contracts import registry, vault
|
|
354
|
+
|
|
355
|
+
registry.admin()
|
|
356
|
+
vault.getApprovedSettlers()
|
|
285
357
|
```
|
|
286
358
|
|
|
287
|
-
**
|
|
359
|
+
With the **Ape** backend, just use `ape console` / `ape run` as usual; it's
|
|
360
|
+
auto-detected whenever Ape is connected to a network:
|
|
288
361
|
|
|
289
|
-
|
|
362
|
+
```shell
|
|
363
|
+
ape console --network ethereum:sepolia:alchemy
|
|
364
|
+
```
|
|
290
365
|
|
|
291
366
|
```python
|
|
292
|
-
In[1]: registry
|
|
293
|
-
|
|
367
|
+
In [1]: from tplus.evm.contracts import registry
|
|
368
|
+
In [2]: registry.getAssets()
|
|
369
|
+
Out[2]: [
|
|
294
370
|
getAssets_return(
|
|
295
371
|
assetAddress=HexBytes("0x000000000000000000000000f08a50178dfcde18524640ea6618a1f965821715"),
|
|
296
372
|
chainId=11155111,
|
|
297
373
|
maxDeposits=100,
|
|
298
374
|
)
|
|
299
375
|
]
|
|
300
|
-
In[
|
|
301
|
-
Out[
|
|
376
|
+
In [3]: registry.admin()
|
|
377
|
+
Out[3]: '0x467a95fC5359edE5d5dDc4f10A1F4B680694858E'
|
|
302
378
|
```
|
|
303
379
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
Sign settlement messages using the structured `Order` type from `tplus.utils.domain`.
|
|
307
|
-
|
|
308
|
-
```python
|
|
309
|
-
from ape import accounts, convert, chain
|
|
310
|
-
from tplus.utils.domain import Order
|
|
311
|
-
from tplus.evm.contracts import vault
|
|
312
|
-
from tplus.utils.user import UserManager
|
|
313
|
-
|
|
314
|
-
# Load your Ethereum account for t+.
|
|
315
|
-
tplus_user = accounts.load("tplus-account")
|
|
316
|
-
|
|
317
|
-
# Load your t+ user (public key).
|
|
318
|
-
user_id = UserManager.load("my_user").public_key
|
|
319
|
-
|
|
320
|
-
# Get the nonce from t+ or the contracts directly.
|
|
321
|
-
nonce = vault.getDepositNonce(tplus_user)
|
|
322
|
-
|
|
323
|
-
order = Order(
|
|
324
|
-
tokenOut="0x62622E77D1349Face943C6e7D5c01C61465FE1dc",
|
|
325
|
-
amountOut=convert("1 ether", int),
|
|
326
|
-
tokenIn="0x58372ab62269A52fA636aD7F200d93999595DCAF",
|
|
327
|
-
amountIn=convert("1 ether", int),
|
|
328
|
-
userId=user_id,
|
|
329
|
-
nonce=nonce,
|
|
330
|
-
validUntil=chain.pending_timestamp,
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
# Use this signature for the settlement.
|
|
334
|
-
signature = tplus_user.sign_message(order).encode_rsv()
|
|
335
|
-
print(signature)
|
|
336
|
-
```
|
|
380
|
+
See [`docs/userguides/contracts.md`](docs/userguides/contracts.md) for the full
|
|
381
|
+
guide, including accounts, settlements, and the higher-level managers.
|
|
@@ -71,6 +71,41 @@ async def run_client():
|
|
|
71
71
|
asyncio.run(run_client())
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
#### Use EVM accounts as T+ accounts
|
|
75
|
+
|
|
76
|
+
An EVM account can back a T+ account: it signs one fixed message and the T+ identity is derived from that signature, so the same wallet always maps to the same T+ account (the one the T+ frontend derives on wallet login).
|
|
77
|
+
|
|
78
|
+
Pass the account itself anywhere a `User` is accepted, whether that is `default_user=`, a per-call `user=`, or a `signer=`:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from ape import accounts
|
|
82
|
+
|
|
83
|
+
account = accounts.load("me")
|
|
84
|
+
|
|
85
|
+
async with OrderBookClient(API_BASE_URL, default_user=account) as client:
|
|
86
|
+
await client.get_user_inventory() # signs as the T+ user behind the account
|
|
87
|
+
await client.create_limit_order(..., user=account) # per-call override
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
An `eth_account` signer works the same way, with no `[evm]` extra:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from eth_account import Account
|
|
94
|
+
|
|
95
|
+
client = OrderBookClient(API_BASE_URL, default_user=Account.from_key("0x..."))
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Use `load_user_from_ape_account` to hold the `User` itself, for example to read its public key or to pick a sub-account. `load_user` loads stored keyfiles by name, so Ape aliases and keyfile names occupy separate namespaces and cannot collide:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from tplus.utils.user import load_user_from_ape_account
|
|
102
|
+
|
|
103
|
+
user = load_user_from_ape_account("me") # by Ape alias (needs the `evm` extra)
|
|
104
|
+
user = load_user_from_ape_account(account) # an already-loaded Ape account
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The result is a normal T+ `User` and signs every request the same way.
|
|
108
|
+
|
|
74
109
|
#### REST API Usage
|
|
75
110
|
|
|
76
111
|
The client offers async methods for common REST endpoints:
|
|
@@ -154,13 +189,28 @@ print(f"Cancel Order Response: {cancel_response}")
|
|
|
154
189
|
# Replace an Order
|
|
155
190
|
# Original Order ID should be from an existing, open order.
|
|
156
191
|
original_order_id_to_replace = "actual-original-order-id" # Replace with a real order ID
|
|
157
|
-
replace_response = await client.replace_order(
|
|
192
|
+
replace_response, revision = await client.replace_order(
|
|
158
193
|
original_order_id=original_order_id_to_replace,
|
|
159
194
|
asset_id=example_asset,
|
|
160
|
-
new_quantity=6, #
|
|
161
|
-
new_price=1050, #
|
|
195
|
+
new_quantity=6, # Effective lifetime-total quantity
|
|
196
|
+
new_price=1050, # Effective limit price
|
|
162
197
|
)
|
|
163
198
|
print(f"Replace Order Response: {replace_response}")
|
|
199
|
+
# `revision` is the replacement's timestamp_ns; a later amend_order must quote it as
|
|
200
|
+
# expected_authorization_revision.
|
|
201
|
+
|
|
202
|
+
# Replace several open orders in one request (up to 50). Each item is signed on its own;
|
|
203
|
+
# the OMS sends one message per market and returns one status per item, in order.
|
|
204
|
+
batch_replace_response = await client.replace_multiple_orders(
|
|
205
|
+
[
|
|
206
|
+
await client.prepare_replace_order_request(
|
|
207
|
+
original_order_id=oid, asset_id=example_asset, new_quantity=6, new_price=price
|
|
208
|
+
)
|
|
209
|
+
for oid, price in [("order-a", 1050), ("order-b", 1040)]
|
|
210
|
+
]
|
|
211
|
+
)
|
|
212
|
+
for status in batch_replace_response.batch_order_status:
|
|
213
|
+
print(f"{status.order_id}: {status.status} {status.reason or ''}")
|
|
164
214
|
```
|
|
165
215
|
|
|
166
216
|
See `examples/rest_usage.py` for a runnable demonstration.
|
|
@@ -198,68 +248,55 @@ async for trade in client.stream_finalized_trades():
|
|
|
198
248
|
|
|
199
249
|
See `examples/websocket_usage.py` for a runnable demonstration using `asyncio.gather` to run multiple streams concurrently.
|
|
200
250
|
|
|
251
|
+
#### Token Recovery
|
|
252
|
+
|
|
253
|
+
When a bearer token stops working before it actually expires (e.g. from a service restart), both transports recover on their own. A REST response or WebSocket handshake rejected with `401` or `403` re-authenticates and retries once; a second rejection is raised to the caller. Requests rejected concurrently share a single re-authentication.
|
|
254
|
+
|
|
201
255
|
### Contracts
|
|
202
256
|
|
|
203
|
-
To interact with the contracts or sign T+ settlement messages,
|
|
257
|
+
To interact with the on-chain T+ contracts (or sign T+ settlement messages),
|
|
258
|
+
install one of the EVM extras:
|
|
204
259
|
|
|
205
260
|
```shell
|
|
206
|
-
pip install tpluspy[evm]
|
|
261
|
+
pip install "tpluspy[evm]" # web3.py backend (no Ape)
|
|
262
|
+
pip install "tpluspy[evm-ape]" # adds the Ape backend on top of [evm]
|
|
207
263
|
```
|
|
208
264
|
|
|
209
|
-
|
|
210
|
-
|
|
265
|
+
The Python API is identical either way. With the **web3.py** backend, point it
|
|
266
|
+
at a node via `WEB3_PROVIDER_URI` (read by `web3.auto`) — or call
|
|
267
|
+
`tplus.evm.use_web3(rpc_url=...)`:
|
|
211
268
|
|
|
212
269
|
```shell
|
|
213
|
-
|
|
270
|
+
export WEB3_PROVIDER_URI="https://arb1.arbitrum.io/rpc"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from tplus.evm.contracts import registry, vault
|
|
275
|
+
|
|
276
|
+
registry.admin()
|
|
277
|
+
vault.getApprovedSettlers()
|
|
214
278
|
```
|
|
215
279
|
|
|
216
|
-
**
|
|
280
|
+
With the **Ape** backend, just use `ape console` / `ape run` as usual; it's
|
|
281
|
+
auto-detected whenever Ape is connected to a network:
|
|
217
282
|
|
|
218
|
-
|
|
283
|
+
```shell
|
|
284
|
+
ape console --network ethereum:sepolia:alchemy
|
|
285
|
+
```
|
|
219
286
|
|
|
220
287
|
```python
|
|
221
|
-
In[1]: registry
|
|
222
|
-
|
|
288
|
+
In [1]: from tplus.evm.contracts import registry
|
|
289
|
+
In [2]: registry.getAssets()
|
|
290
|
+
Out[2]: [
|
|
223
291
|
getAssets_return(
|
|
224
292
|
assetAddress=HexBytes("0x000000000000000000000000f08a50178dfcde18524640ea6618a1f965821715"),
|
|
225
293
|
chainId=11155111,
|
|
226
294
|
maxDeposits=100,
|
|
227
295
|
)
|
|
228
296
|
]
|
|
229
|
-
In[
|
|
230
|
-
Out[
|
|
297
|
+
In [3]: registry.admin()
|
|
298
|
+
Out[3]: '0x467a95fC5359edE5d5dDc4f10A1F4B680694858E'
|
|
231
299
|
```
|
|
232
300
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
Sign settlement messages using the structured `Order` type from `tplus.utils.domain`.
|
|
236
|
-
|
|
237
|
-
```python
|
|
238
|
-
from ape import accounts, convert, chain
|
|
239
|
-
from tplus.utils.domain import Order
|
|
240
|
-
from tplus.evm.contracts import vault
|
|
241
|
-
from tplus.utils.user import UserManager
|
|
242
|
-
|
|
243
|
-
# Load your Ethereum account for t+.
|
|
244
|
-
tplus_user = accounts.load("tplus-account")
|
|
245
|
-
|
|
246
|
-
# Load your t+ user (public key).
|
|
247
|
-
user_id = UserManager.load("my_user").public_key
|
|
248
|
-
|
|
249
|
-
# Get the nonce from t+ or the contracts directly.
|
|
250
|
-
nonce = vault.getDepositNonce(tplus_user)
|
|
251
|
-
|
|
252
|
-
order = Order(
|
|
253
|
-
tokenOut="0x62622E77D1349Face943C6e7D5c01C61465FE1dc",
|
|
254
|
-
amountOut=convert("1 ether", int),
|
|
255
|
-
tokenIn="0x58372ab62269A52fA636aD7F200d93999595DCAF",
|
|
256
|
-
amountIn=convert("1 ether", int),
|
|
257
|
-
userId=user_id,
|
|
258
|
-
nonce=nonce,
|
|
259
|
-
validUntil=chain.pending_timestamp,
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
# Use this signature for the settlement.
|
|
263
|
-
signature = tplus_user.sign_message(order).encode_rsv()
|
|
264
|
-
print(signature)
|
|
265
|
-
```
|
|
301
|
+
See [`docs/userguides/contracts.md`](docs/userguides/contracts.md) for the full
|
|
302
|
+
guide, including accounts, settlements, and the higher-level managers.
|
|
@@ -8,7 +8,7 @@ long_description = (here / "README.md").read_text(encoding="utf-8")
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name="tpluspy",
|
|
11
|
-
version="0.
|
|
11
|
+
version="0.5.0",
|
|
12
12
|
description="""tpluspy: Client utilities for interacting with tplus""",
|
|
13
13
|
long_description=long_description,
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
@@ -34,6 +34,7 @@ setup(
|
|
|
34
34
|
python_requires=">=3.10,<4",
|
|
35
35
|
extras_require={
|
|
36
36
|
"test": [
|
|
37
|
+
"eth-account>=0.13.0,<0.14",
|
|
37
38
|
"pytest>=8.0",
|
|
38
39
|
"pytest-timeout>=2.2.0,<3",
|
|
39
40
|
"pytest-mock",
|
|
@@ -41,6 +42,10 @@ setup(
|
|
|
41
42
|
"anyio>=4",
|
|
42
43
|
"pytest-anyio>=0.0.0",
|
|
43
44
|
"trio>=0.24",
|
|
45
|
+
# In-process EVM backend used by the web3-backend tests. ``py-evm``
|
|
46
|
+
# is what actually executes the EVM; ``eth-tester`` only ships
|
|
47
|
+
# pre-releases on PyPI so allow them.
|
|
48
|
+
"eth-tester[py-evm]>=0.12.0b1",
|
|
44
49
|
],
|
|
45
50
|
"lint": [
|
|
46
51
|
"ruff>=0.11.7,<0.16",
|
|
@@ -52,11 +57,18 @@ setup(
|
|
|
52
57
|
"twine",
|
|
53
58
|
],
|
|
54
59
|
"evm": [
|
|
55
|
-
"ape-tokens",
|
|
56
60
|
"click",
|
|
57
61
|
"eip712>=0.3.1",
|
|
58
|
-
"eth-
|
|
62
|
+
"eth-account>=0.13.0,<0.14",
|
|
63
|
+
"eth-keys>=0.6,<0.8",
|
|
59
64
|
"hexbytes>=1.3.1,<2",
|
|
65
|
+
"pyyaml>=6,<7",
|
|
66
|
+
"web3>=7.0,<8",
|
|
67
|
+
],
|
|
68
|
+
"evm-ape": [
|
|
69
|
+
"tpluspy[evm]",
|
|
70
|
+
"ape-tokens",
|
|
71
|
+
"eth-ape>=0.8.49,<0.9",
|
|
60
72
|
],
|
|
61
73
|
"docs": [
|
|
62
74
|
"Sphinx>=7.0,<9",
|
|
@@ -72,7 +84,7 @@ setup(
|
|
|
72
84
|
zip_safe=False,
|
|
73
85
|
keywords="ethereum",
|
|
74
86
|
packages=find_packages(exclude=["tests", "tests.*"]),
|
|
75
|
-
package_data={"
|
|
87
|
+
package_data={"tplus": ["py.typed", "evm/manifests/*"]},
|
|
76
88
|
classifiers=[
|
|
77
89
|
"Development Status :: 5 - Production/Stable",
|
|
78
90
|
"Intended Audience :: Developers",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
|
-
from typing import Any, cast
|
|
4
3
|
|
|
5
4
|
import click
|
|
6
5
|
|
|
@@ -19,6 +18,23 @@ from tplus.cli_tools import (
|
|
|
19
18
|
output_format_option,
|
|
20
19
|
render,
|
|
21
20
|
)
|
|
21
|
+
from tplus.model.market import MarketResponse
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async def _collect_markets(
|
|
25
|
+
client, page_number: int | None, limit: int | None
|
|
26
|
+
) -> list[MarketResponse]:
|
|
27
|
+
"""The requested page, or every page when the caller did not pick one."""
|
|
28
|
+
page = await client.get_markets(page=page_number, limit=limit)
|
|
29
|
+
if page_number is not None:
|
|
30
|
+
return page.markets
|
|
31
|
+
|
|
32
|
+
markets = list(page.markets)
|
|
33
|
+
while page.next_page is not None:
|
|
34
|
+
page = await client.get_markets(page=page.next_page, limit=limit)
|
|
35
|
+
markets.extend(page.markets)
|
|
36
|
+
|
|
37
|
+
return markets
|
|
22
38
|
|
|
23
39
|
|
|
24
40
|
@click.group()
|
|
@@ -60,35 +76,43 @@ def _get(cli_ctx: CLIContext, asset_id: str):
|
|
|
60
76
|
@tplus_account_option()
|
|
61
77
|
@output_format_option()
|
|
62
78
|
@no_pager_option()
|
|
79
|
+
@click.option("--page", "page_number", type=int)
|
|
80
|
+
@click.option("--limit", type=int)
|
|
63
81
|
@pass_cli_context
|
|
64
|
-
def _list(
|
|
65
|
-
|
|
82
|
+
def _list(
|
|
83
|
+
cli_ctx: CLIContext,
|
|
84
|
+
output_format: str,
|
|
85
|
+
no_pager: bool,
|
|
86
|
+
page_number: int | None,
|
|
87
|
+
limit: int | None,
|
|
88
|
+
):
|
|
89
|
+
"""List all markets, following pagination unless `--page` picks one."""
|
|
66
90
|
client = cli_ctx.orderbook_client()
|
|
67
|
-
|
|
91
|
+
markets = asyncio.run(_collect_markets(client, page_number, limit))
|
|
68
92
|
if output_format == "raw":
|
|
69
|
-
|
|
93
|
+
raw = [market.model_dump(by_alias=True) for market in markets]
|
|
94
|
+
click.echo(json.dumps(raw, indent=2, default=str))
|
|
70
95
|
return
|
|
71
96
|
|
|
72
97
|
records = []
|
|
73
|
-
for market in
|
|
74
|
-
fee_schedule = market.
|
|
75
|
-
asset_metadata = asset_metadata_dict(market.
|
|
98
|
+
for market in markets:
|
|
99
|
+
fee_schedule = market.fee_schedule
|
|
100
|
+
asset_metadata = asset_metadata_dict(str(market.asset_id)) or {}
|
|
76
101
|
records.append(
|
|
77
102
|
{
|
|
78
|
-
"asset_id": market.
|
|
103
|
+
"asset_id": str(market.asset_id),
|
|
79
104
|
"symbol": asset_metadata.get("symbol"),
|
|
80
105
|
"asset_class": asset_metadata.get("asset_class"),
|
|
81
106
|
"representations": asset_metadata.get("representations"),
|
|
82
|
-
"price_decimals": market.
|
|
83
|
-
"quantity_decimals": market.
|
|
84
|
-
"max_leverage": market.
|
|
85
|
-
"isolated_only": market.
|
|
86
|
-
"tick_size": market.
|
|
87
|
-
"min_order_size": market.
|
|
88
|
-
"fee_account": fee_schedule.
|
|
107
|
+
"price_decimals": market.book_price_decimals,
|
|
108
|
+
"quantity_decimals": market.book_quantity_decimals,
|
|
109
|
+
"max_leverage": market.max_leverage,
|
|
110
|
+
"isolated_only": market.isolated_only,
|
|
111
|
+
"tick_size": market.tick_size,
|
|
112
|
+
"min_order_size": market.min_order_size,
|
|
113
|
+
"fee_account": fee_schedule.fee_account if fee_schedule else None,
|
|
89
114
|
"fee_tiers": (
|
|
90
|
-
f"{len(fee_schedule.
|
|
91
|
-
f"{len(fee_schedule.get('per_asset') or [])} per-asset"
|
|
115
|
+
f"{len(fee_schedule.global_)} global / {len(fee_schedule.per_asset)} per-asset"
|
|
92
116
|
if fee_schedule
|
|
93
117
|
else None
|
|
94
118
|
),
|
|
@@ -97,6 +121,35 @@ def _list(cli_ctx: CLIContext, output_format: str, no_pager: bool):
|
|
|
97
121
|
render(records, output_format, no_pager=no_pager)
|
|
98
122
|
|
|
99
123
|
|
|
124
|
+
@markets.command("symbol-map")
|
|
125
|
+
@orderbook_url_option()
|
|
126
|
+
@ignore_ssl_option()
|
|
127
|
+
@tplus_account_option()
|
|
128
|
+
@output_format_option()
|
|
129
|
+
@no_pager_option()
|
|
130
|
+
@pass_cli_context
|
|
131
|
+
def _symbol_map(cli_ctx: CLIContext, output_format: str, no_pager: bool):
|
|
132
|
+
"""Show the canonical asset table served by the OMS."""
|
|
133
|
+
client = cli_ctx.orderbook_client()
|
|
134
|
+
symbol_map = asyncio.run(client.get_markets(include_symbol_map=True)).symbol_map or {}
|
|
135
|
+
if output_format == "raw":
|
|
136
|
+
raw = {str(index): asset.model_dump() for index, asset in symbol_map.items()}
|
|
137
|
+
click.echo(json.dumps(raw, indent=2))
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
records = [
|
|
141
|
+
{
|
|
142
|
+
"index": index,
|
|
143
|
+
"symbol": asset.symbol,
|
|
144
|
+
"name": asset.name,
|
|
145
|
+
"asset_class": asset.asset_class,
|
|
146
|
+
"representations": ", ".join(asset.representations),
|
|
147
|
+
}
|
|
148
|
+
for index, asset in sorted(symbol_map.items())
|
|
149
|
+
]
|
|
150
|
+
render(records, output_format, no_pager=no_pager)
|
|
151
|
+
|
|
152
|
+
|
|
100
153
|
@markets.command("depth")
|
|
101
154
|
@market_data_url_option()
|
|
102
155
|
@ignore_ssl_option()
|
|
@@ -117,7 +117,7 @@ def _replace(
|
|
|
117
117
|
from tplus.model.asset_identifier import AssetIdentifier
|
|
118
118
|
|
|
119
119
|
client = cli_ctx.orderbook_client()
|
|
120
|
-
response = asyncio.run(
|
|
120
|
+
response, _revision = asyncio.run(
|
|
121
121
|
client.replace_order(
|
|
122
122
|
original_order_id=order_id,
|
|
123
123
|
asset_id=AssetIdentifier(asset_id),
|
|
@@ -78,7 +78,7 @@ def _build_manager(
|
|
|
78
78
|
vault = cast("DepositVault", DepositVault.at(vault_address)) if vault_address else None
|
|
79
79
|
return SettlementManager(
|
|
80
80
|
default_user=cli_ctx.load_user(),
|
|
81
|
-
|
|
81
|
+
account=signer,
|
|
82
82
|
oms_client=cli_ctx.orderbook_client(),
|
|
83
83
|
vault=vault,
|
|
84
84
|
)
|