tpluspy 0.2.3__tar.gz → 0.2.4__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.2.3 → tpluspy-0.2.4}/PKG-INFO +23 -1
- {tpluspy-0.2.3 → tpluspy-0.2.4}/README.md +22 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/setup.py +1 -1
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/_context.py +59 -20
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/env.py +11 -1
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/markets.py +5 -0
- tpluspy-0.2.4/tplus/asset_metadata.py +116 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tpluspy.egg-info/PKG-INFO +23 -1
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tpluspy.egg-info/SOURCES.txt +1 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/pyproject.toml +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/setup.cfg +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/__main__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/accounts.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/assets.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/balance.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/debug.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/decimals.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/deposit.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/orders.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/params.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/settle.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/sign.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/stream.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/trades.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/vaults.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/withdraw.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/_cli/withdrawal.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/cli_tools/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/cli_tools/options.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/cli_tools/output.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/auth.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/base.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/blockchain.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/clearingengine/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/clearingengine/admin.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/clearingengine/admin_settlement.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/clearingengine/assetregistry.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/clearingengine/base.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/clearingengine/cross_venue.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/clearingengine/decimal.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/clearingengine/vault.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/market_data.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/oms/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/oms/assetregistry.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/oms/oms_admin.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/orderbook.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/client/withdrawal.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/constants.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/abi.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/address.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/constants.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/contracts.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/dev/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/dev/contracts.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/dev/env.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/dev/token.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/exceptions.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/chaindata.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/credential_manager.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/deposit.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/evm.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/registry.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/settle.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/vault.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/managers/withdraw.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/evm/utils.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/exceptions.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/logger.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/managers/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/managers/base.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/approval.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/asset_identifier.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/batch_order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/cancel_order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/chain_address.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/close_all_positions_preview.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/config.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/interest_rates.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/klines.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/limit_order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/market.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/market_order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/multisig.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/order_trigger.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/orderbook.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/position.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/replace_order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/risk_parameters.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/settlement.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/signed_message.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/trades.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/types.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/user_event.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/user_inventory.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/user_margin.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/user_simulated_margin.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/user_solvency.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/model/withdrawal.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/py.typed +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/types.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/address.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/amount.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/bytes32.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/decimals.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/domain.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/hex.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/limit_order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/market_order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/operator.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/replace_order.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/serializers.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/signing.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/timeout.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/user/__init__.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/user/decrypt.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/user/ed_keyfile.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/user/manager.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/user/model.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/user/token_cache.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tplus/utils/user/validate.py +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tpluspy.egg-info/dependency_links.txt +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tpluspy.egg-info/entry_points.txt +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tpluspy.egg-info/not-zip-safe +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/tpluspy.egg-info/requires.txt +0 -0
- {tpluspy-0.2.3 → tpluspy-0.2.4}/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.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: tpluspy: Client utilities for interacting with tplus
|
|
5
5
|
Home-page: https://github.com/tpluslabs/tpluspy
|
|
6
6
|
Author: TPlus Labs
|
|
@@ -81,6 +81,28 @@ To install, use either `pip` or `uv pip`:
|
|
|
81
81
|
uv pip install -e .
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
## Onboarding with Codex CLI
|
|
85
|
+
|
|
86
|
+
This repo includes a Codex-discoverable onboarding skill at
|
|
87
|
+
`.agents/skills/onboard` for connecting an EVM wallet key to T+, checking whether
|
|
88
|
+
it already controls a T+ account, and guiding account creation when needed.
|
|
89
|
+
|
|
90
|
+
From the repo root:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
codex
|
|
94
|
+
/skills
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Choose `onboard`, or invoke it directly with:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
$onboard onboard me to T+
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The flow reads `TPLUS_PRIVATE_KEY` and `TPLUS_API_BASE_URL` from `.env`. Do not
|
|
104
|
+
paste private keys into chat.
|
|
105
|
+
|
|
84
106
|
## CLI
|
|
85
107
|
|
|
86
108
|
`tpluspy` installs a `tplus` command for interacting with T+ services from the
|
|
@@ -10,6 +10,28 @@ To install, use either `pip` or `uv pip`:
|
|
|
10
10
|
uv pip install -e .
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## Onboarding with Codex CLI
|
|
14
|
+
|
|
15
|
+
This repo includes a Codex-discoverable onboarding skill at
|
|
16
|
+
`.agents/skills/onboard` for connecting an EVM wallet key to T+, checking whether
|
|
17
|
+
it already controls a T+ account, and guiding account creation when needed.
|
|
18
|
+
|
|
19
|
+
From the repo root:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
codex
|
|
23
|
+
/skills
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Choose `onboard`, or invoke it directly with:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
$onboard onboard me to T+
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The flow reads `TPLUS_PRIVATE_KEY` and `TPLUS_API_BASE_URL` from `.env`. Do not
|
|
33
|
+
paste private keys into chat.
|
|
34
|
+
|
|
13
35
|
## CLI
|
|
14
36
|
|
|
15
37
|
`tpluspy` installs a `tplus` command for interacting with T+ services from the
|
|
@@ -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.2.
|
|
11
|
+
version="0.2.4",
|
|
12
12
|
description="""tpluspy: Client utilities for interacting with tplus""",
|
|
13
13
|
long_description=long_description,
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from collections.abc import Callable
|
|
2
3
|
from functools import cached_property
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import TYPE_CHECKING
|
|
6
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
5
7
|
|
|
6
8
|
import click
|
|
7
9
|
|
|
@@ -18,12 +20,38 @@ if TYPE_CHECKING:
|
|
|
18
20
|
_AUTH_CACHE_DIR = Path.home() / ".tplus" / "auth"
|
|
19
21
|
|
|
20
22
|
|
|
23
|
+
def _derive_market_data_base_url(oms_base_url: str) -> str:
|
|
24
|
+
"""Derive the market-data-service URL from the OMS URL (swap the host label
|
|
25
|
+
``oms``->``mds``). Returns the input unchanged for non-oms hosts (e.g. localhost)."""
|
|
26
|
+
parsed = urlsplit(oms_base_url)
|
|
27
|
+
host = parsed.hostname
|
|
28
|
+
if host is None:
|
|
29
|
+
return oms_base_url
|
|
30
|
+
if host.startswith("oms."):
|
|
31
|
+
mds_host = f"mds.{host.removeprefix('oms.')}"
|
|
32
|
+
elif host.startswith("oms-"):
|
|
33
|
+
mds_host = f"mds-{host.removeprefix('oms-')}"
|
|
34
|
+
else:
|
|
35
|
+
return oms_base_url
|
|
36
|
+
userinfo = ""
|
|
37
|
+
if parsed.username is not None:
|
|
38
|
+
userinfo = parsed.username
|
|
39
|
+
if parsed.password is not None:
|
|
40
|
+
userinfo = f"{userinfo}:{parsed.password}"
|
|
41
|
+
userinfo = f"{userinfo}@"
|
|
42
|
+
port = f":{parsed.port}" if parsed.port is not None else ""
|
|
43
|
+
return urlunsplit(
|
|
44
|
+
(parsed.scheme, f"{userinfo}{mds_host}{port}", parsed.path, parsed.query, parsed.fragment)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
21
48
|
# ``dict`` base mirrors ``ape.cli.options.ApeCliContextObject`` so ape's
|
|
22
49
|
# ``network_option`` callback can do ``ctx.obj["network"] = value`` without
|
|
23
50
|
# importing ape here (which would slow down ``tplus --help``).
|
|
24
51
|
class CLIContext(dict):
|
|
25
52
|
def __init__(self):
|
|
26
53
|
super().__init__()
|
|
54
|
+
self.api_base_url: str | None = None
|
|
27
55
|
self.orderbook_base_url: str | None = None
|
|
28
56
|
self.clearing_base_url: str | None = None
|
|
29
57
|
self.market_data_base_url: str | None = None
|
|
@@ -51,22 +79,44 @@ class CLIContext(dict):
|
|
|
51
79
|
manager = self.user_manager
|
|
52
80
|
return manager._default_user or next(iter(manager.usernames), None)
|
|
53
81
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
def _api_base_url(self) -> str | None:
|
|
83
|
+
"""The single T+ base URL (the OMS). Per-service URLs derive from it; an
|
|
84
|
+
explicit per-service flag / env var overrides it."""
|
|
85
|
+
return self.api_base_url or os.environ.get("TPLUS_API_BASE_URL")
|
|
86
|
+
|
|
87
|
+
def _resolved_orderbook_url(self) -> str:
|
|
88
|
+
# The OMS host *is* the orderbook service, so TPLUS_API_BASE_URL is used directly.
|
|
89
|
+
url = self.orderbook_base_url or self._api_base_url()
|
|
90
|
+
if not url:
|
|
91
|
+
raise click.UsageError(
|
|
92
|
+
"No T+ base URL. Set TPLUS_API_BASE_URL (the OMS URL — the single T+ URL "
|
|
93
|
+
"everything derives from), or pass --orderbook-base-url / set "
|
|
94
|
+
"TPLUS_ORDERBOOK_BASE_URL to override."
|
|
95
|
+
)
|
|
96
|
+
return url
|
|
97
|
+
|
|
98
|
+
def _resolved_market_data_url(self) -> str:
|
|
99
|
+
if self.market_data_base_url:
|
|
100
|
+
return self.market_data_base_url
|
|
101
|
+
api = self._api_base_url()
|
|
102
|
+
if not api:
|
|
58
103
|
raise click.UsageError(
|
|
59
|
-
"No
|
|
60
|
-
"
|
|
104
|
+
"No T+ base URL. Set TPLUS_API_BASE_URL (the market-data URL is derived "
|
|
105
|
+
"by swapping the host label oms->mds), or pass --market-data-base-url / "
|
|
106
|
+
"set TPLUS_MARKET_DATA_BASE_URL to override."
|
|
61
107
|
)
|
|
108
|
+
return _derive_market_data_base_url(api)
|
|
62
109
|
|
|
110
|
+
def orderbook_client(
|
|
111
|
+
self, alias: str | None = None, *, anonymous: bool = False
|
|
112
|
+
) -> "OrderBookClient":
|
|
63
113
|
from tplus.client.auth import Auth
|
|
64
114
|
from tplus.client.orderbook import OrderBookClient
|
|
65
115
|
from tplus.utils.user.model import User
|
|
66
116
|
|
|
67
117
|
user = User() if anonymous else self.load_user(alias)
|
|
68
118
|
return OrderBookClient(
|
|
69
|
-
base_url=self.
|
|
119
|
+
base_url=self._resolved_orderbook_url(),
|
|
70
120
|
default_user=user,
|
|
71
121
|
auth=Auth(cache_dir=_AUTH_CACHE_DIR),
|
|
72
122
|
insecure_ssl=self.ignore_ssl,
|
|
@@ -92,12 +142,7 @@ class CLIContext(dict):
|
|
|
92
142
|
)
|
|
93
143
|
|
|
94
144
|
def _oms_base_url(self) -> str:
|
|
95
|
-
|
|
96
|
-
raise click.UsageError(
|
|
97
|
-
"No orderbook base URL specified. "
|
|
98
|
-
"Pass --orderbook-base-url or set TPLUS_ORDERBOOK_BASE_URL."
|
|
99
|
-
)
|
|
100
|
-
return self.orderbook_base_url
|
|
145
|
+
return self._resolved_orderbook_url()
|
|
101
146
|
|
|
102
147
|
def withdrawal_client(self, alias: str | None = None) -> "WithdrawalClient":
|
|
103
148
|
from tplus.client.withdrawal import WithdrawalClient
|
|
@@ -120,16 +165,10 @@ class CLIContext(dict):
|
|
|
120
165
|
return BlockchainClient(base_url=self.blockchain_base_url, insecure_ssl=self.ignore_ssl)
|
|
121
166
|
|
|
122
167
|
def market_data_client(self) -> "MarketDataClient":
|
|
123
|
-
if not self.market_data_base_url:
|
|
124
|
-
raise click.UsageError(
|
|
125
|
-
"No market-data base URL specified. "
|
|
126
|
-
"Pass --market-data-base-url or set TPLUS_MARKET_DATA_BASE_URL."
|
|
127
|
-
)
|
|
128
|
-
|
|
129
168
|
from tplus.client.market_data import MarketDataClient
|
|
130
169
|
|
|
131
170
|
return MarketDataClient(
|
|
132
|
-
base_url=self.
|
|
171
|
+
base_url=self._resolved_market_data_url(),
|
|
133
172
|
insecure_ssl=self.ignore_ssl,
|
|
134
173
|
)
|
|
135
174
|
|
|
@@ -20,7 +20,17 @@ def env(output_format: str):
|
|
|
20
20
|
"""Show CLI-relevant environment variables and their current values."""
|
|
21
21
|
entries: list[tuple[str, str, str]] = [
|
|
22
22
|
("TPLUS_ACCOUNT", _default_account(), "Default local account alias."),
|
|
23
|
-
(
|
|
23
|
+
(
|
|
24
|
+
"TPLUS_API_BASE_URL",
|
|
25
|
+
"",
|
|
26
|
+
"Single T+ base URL (OMS); orderbook + market-data derive from it.",
|
|
27
|
+
),
|
|
28
|
+
("TPLUS_ORDERBOOK_BASE_URL", "", "Override: orderbook service base URL."),
|
|
29
|
+
(
|
|
30
|
+
"TPLUS_MARKET_DATA_BASE_URL",
|
|
31
|
+
"",
|
|
32
|
+
"Override: market-data URL (else derived from API URL, oms->mds).",
|
|
33
|
+
),
|
|
24
34
|
("TPLUS_CLEARING_BASE_URL", "", "Clearing engine base URL."),
|
|
25
35
|
("TPLUS_IGNORE_SSL", "false", "Skip TLS certificate verification."),
|
|
26
36
|
("TPLUS_OUTPUT_FORMAT", "table", "Default output format (table | json)."),
|
|
@@ -11,6 +11,7 @@ from tplus._cli._context import (
|
|
|
11
11
|
pass_cli_context,
|
|
12
12
|
tplus_account_option,
|
|
13
13
|
)
|
|
14
|
+
from tplus.asset_metadata import asset_metadata_dict
|
|
14
15
|
from tplus.cli_tools import (
|
|
15
16
|
echo_with_pager,
|
|
16
17
|
ignore_ssl_option,
|
|
@@ -71,9 +72,13 @@ def _list(cli_ctx: CLIContext, output_format: str, no_pager: bool):
|
|
|
71
72
|
records = []
|
|
72
73
|
for market in response or []:
|
|
73
74
|
fee_schedule = market.get("fee_schedule") or {}
|
|
75
|
+
asset_metadata = asset_metadata_dict(market.get("asset_id")) or {}
|
|
74
76
|
records.append(
|
|
75
77
|
{
|
|
76
78
|
"asset_id": market.get("asset_id"),
|
|
79
|
+
"symbol": asset_metadata.get("symbol"),
|
|
80
|
+
"asset_class": asset_metadata.get("asset_class"),
|
|
81
|
+
"representations": asset_metadata.get("representations"),
|
|
77
82
|
"price_decimals": market.get("book_price_decimals"),
|
|
78
83
|
"quantity_decimals": market.get("book_quantity_decimals"),
|
|
79
84
|
"max_leverage": market.get("max_leverage"),
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Static T+ asset metadata used for display-only symbol resolution.
|
|
2
|
+
|
|
3
|
+
The protocol identifies assets by ``AssetIdentifier`` values, not symbols. This
|
|
4
|
+
module is a temporary local snapshot so CLI and onboarding output can show the
|
|
5
|
+
canonical symbol next to known production indexes and chain-address aliases.
|
|
6
|
+
|
|
7
|
+
Source snapshot: ``tplus-risk-params`` ``main:environments/prod/params.json``.
|
|
8
|
+
Do not use this for risk checks; read live registry/market endpoints for trading
|
|
9
|
+
parameters.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from tplus.model.asset_identifier import AssetIdentifier
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class AssetMetadata:
|
|
22
|
+
index: int
|
|
23
|
+
symbol: str
|
|
24
|
+
asset_class: str
|
|
25
|
+
representations: tuple[str, ...]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
CANONICAL_ASSETS: dict[int, AssetMetadata] = {
|
|
29
|
+
0: AssetMetadata(0, "USDC", "USD", ("USDC", "USDT")),
|
|
30
|
+
1: AssetMetadata(1, "ETH", "ETH", ("WETH",)),
|
|
31
|
+
2: AssetMetadata(2, "BTC", "BTC", ("WBTC",)),
|
|
32
|
+
3: AssetMetadata(3, "ARB", "ARB", ("ARB",)),
|
|
33
|
+
4: AssetMetadata(4, "MON", "MON", ("MON",)),
|
|
34
|
+
5: AssetMetadata(5, "GOLD", "GOLD", ("XAUT",)),
|
|
35
|
+
6: AssetMetadata(6, "CIGS", "CIGS", ("CIGS",)),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
CHAIN_ASSET_INDEXES: dict[str, int] = {
|
|
40
|
+
# USDC / USDT -> USD class.
|
|
41
|
+
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000@000000000000000001": 0,
|
|
42
|
+
"af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000@00000000000000a4b1": 0,
|
|
43
|
+
"754704bc059f8c67012fed69bc8a327a5aafb603000000000000000000000000@00000000000000008f": 0,
|
|
44
|
+
"dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000@000000000000000001": 0,
|
|
45
|
+
"833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000@000000000000002105": 0,
|
|
46
|
+
# ETH.
|
|
47
|
+
"82af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000@00000000000000a4b1": 1,
|
|
48
|
+
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000@000000000000000001": 1,
|
|
49
|
+
"ee8c0e9f1bffb4eb878d8f15f368a02a35481242000000000000000000000000@00000000000000008f": 1,
|
|
50
|
+
"4200000000000000000000000000000000000006000000000000000000000000@000000000000002105": 1,
|
|
51
|
+
# BTC.
|
|
52
|
+
"2260fac5e5542a773aa44fbcfedf7c193bc2c599000000000000000000000000@000000000000000001": 2,
|
|
53
|
+
"2f2a2543b76a4166549f7aab2e75bef0aefc5b0f000000000000000000000000@00000000000000a4b1": 2,
|
|
54
|
+
"0555e30da8f98308edb960aa94c0db47230d2b9c000000000000000000000000@00000000000000008f": 2,
|
|
55
|
+
# ARB.
|
|
56
|
+
"b50721bcf8d664c30412cfbc6cf7a15145234ad1000000000000000000000000@000000000000000001": 3,
|
|
57
|
+
"912ce59144191c1204e64559fe8253a0e49e6548000000000000000000000000@00000000000000a4b1": 3,
|
|
58
|
+
# MON.
|
|
59
|
+
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000@00000000000000008f": 4,
|
|
60
|
+
# GOLD / XAUT.
|
|
61
|
+
"68749665ff8d2d112fa859aa293f07a622782f38000000000000000000000000@000000000000000001": 5,
|
|
62
|
+
# CIGS.
|
|
63
|
+
"2e9126f66e386879f312287a419b0f70004c3f5b000000000000000000000000@000000000000002105": 6,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _asset_id_key(asset_id: Any) -> str:
|
|
68
|
+
if isinstance(asset_id, AssetIdentifier):
|
|
69
|
+
return str(asset_id)
|
|
70
|
+
if isinstance(asset_id, int):
|
|
71
|
+
return str(asset_id)
|
|
72
|
+
if isinstance(asset_id, dict):
|
|
73
|
+
return str(AssetIdentifier.model_validate(asset_id))
|
|
74
|
+
return str(asset_id).lower().removeprefix("0x")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def asset_index(asset_id: Any) -> int | None:
|
|
78
|
+
"""Return the canonical asset index for a known index/address identifier."""
|
|
79
|
+
key = _asset_id_key(asset_id)
|
|
80
|
+
if key.isnumeric():
|
|
81
|
+
return int(key)
|
|
82
|
+
if "@" in key:
|
|
83
|
+
try:
|
|
84
|
+
key = str(AssetIdentifier(key))
|
|
85
|
+
except ValueError:
|
|
86
|
+
pass
|
|
87
|
+
return CHAIN_ASSET_INDEXES.get(key)
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_asset_metadata(asset_id: Any) -> AssetMetadata | None:
|
|
92
|
+
index = asset_index(asset_id)
|
|
93
|
+
if index is None:
|
|
94
|
+
return None
|
|
95
|
+
return CANONICAL_ASSETS.get(index)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def asset_label(asset_id: Any) -> str:
|
|
99
|
+
"""Human label for a known asset identifier, falling back without guessing."""
|
|
100
|
+
key = _asset_id_key(asset_id)
|
|
101
|
+
metadata = get_asset_metadata(key)
|
|
102
|
+
if metadata is None:
|
|
103
|
+
return f"asset {key}"
|
|
104
|
+
return f"{metadata.symbol} (asset {key})"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def asset_metadata_dict(asset_id: Any) -> dict[str, Any] | None:
|
|
108
|
+
metadata = get_asset_metadata(asset_id)
|
|
109
|
+
if metadata is None:
|
|
110
|
+
return None
|
|
111
|
+
return {
|
|
112
|
+
"index": metadata.index,
|
|
113
|
+
"symbol": metadata.symbol,
|
|
114
|
+
"asset_class": metadata.asset_class,
|
|
115
|
+
"representations": list(metadata.representations),
|
|
116
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tpluspy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: tpluspy: Client utilities for interacting with tplus
|
|
5
5
|
Home-page: https://github.com/tpluslabs/tpluspy
|
|
6
6
|
Author: TPlus Labs
|
|
@@ -81,6 +81,28 @@ To install, use either `pip` or `uv pip`:
|
|
|
81
81
|
uv pip install -e .
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
## Onboarding with Codex CLI
|
|
85
|
+
|
|
86
|
+
This repo includes a Codex-discoverable onboarding skill at
|
|
87
|
+
`.agents/skills/onboard` for connecting an EVM wallet key to T+, checking whether
|
|
88
|
+
it already controls a T+ account, and guiding account creation when needed.
|
|
89
|
+
|
|
90
|
+
From the repo root:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
codex
|
|
94
|
+
/skills
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Choose `onboard`, or invoke it directly with:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
$onboard onboard me to T+
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The flow reads `TPLUS_PRIVATE_KEY` and `TPLUS_API_BASE_URL` from `.env`. Do not
|
|
104
|
+
paste private keys into chat.
|
|
105
|
+
|
|
84
106
|
## CLI
|
|
85
107
|
|
|
86
108
|
`tpluspy` installs a `tplus` command for interacting with T+ services from the
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|