tpluspy 0.3.0__tar.gz → 0.3.2__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.0 → tpluspy-0.3.2}/PKG-INFO +2 -2
- {tpluspy-0.3.0 → tpluspy-0.3.2}/README.md +1 -1
- {tpluspy-0.3.0 → tpluspy-0.3.2}/setup.py +1 -1
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/markets.py +7 -1
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/market_data.py +22 -20
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/oms/oms_admin.py +3 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/orderbook.py +8 -1
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/klines.py +45 -12
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/replace_order.py +4 -1
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/replace_order.py +1 -5
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/signing.py +3 -6
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tpluspy.egg-info/PKG-INFO +2 -2
- {tpluspy-0.3.0 → tpluspy-0.3.2}/pyproject.toml +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/setup.cfg +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/__main__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/_context.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/accounts.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/assets.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/balance.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/debug.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/decimals.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/deposit.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/env.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/orders.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/params.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/settle.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/sign.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/stream.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/trades.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/vaults.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/withdraw.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/_cli/withdrawal.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/asset_metadata.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/cli_tools/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/cli_tools/options.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/cli_tools/output.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/api.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/auth.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/base.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/blockchain.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/clearingengine/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/clearingengine/admin.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/clearingengine/admin_settlement.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/clearingengine/assetregistry.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/clearingengine/base.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/clearingengine/cross_venue.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/clearingengine/decimal.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/clearingengine/vault.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/oms/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/oms/assetregistry.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/client/withdrawal.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/constants.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/abi.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/address.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/constants.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/contracts.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/dev/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/dev/contracts.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/dev/env.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/dev/token.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/exceptions.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/chaindata.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/credential_manager.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/deposit.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/evm.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/registry.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/settle.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/vault.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/managers/withdraw.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/evm/utils.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/exceptions.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/logger.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/managers/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/managers/base.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/approval.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/asset_identifier.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/batch_order.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/cancel_order.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/chain_address.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/close_all_positions_preview.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/config.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/interest_rates.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/limit_order.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/market.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/market_order.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/multisig.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/order.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/order_id.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/order_trigger.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/orderbook.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/pagination.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/position.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/risk_parameters.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/settlement.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/signed_message.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/trades.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/types.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/user_event.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/user_inventory.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/user_margin.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/user_simulated_margin.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/user_solvency.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/model/withdrawal.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/py.typed +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/types.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/address.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/amount.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/bytes32.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/decimals.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/domain.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/hex.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/limit_order.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/market_order.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/operator.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/serializers.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/timeout.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/user/__init__.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/user/decrypt.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/user/ed_keyfile.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/user/manager.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/user/model.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/user/token_cache.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tplus/utils/user/validate.py +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tpluspy.egg-info/SOURCES.txt +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tpluspy.egg-info/dependency_links.txt +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tpluspy.egg-info/entry_points.txt +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tpluspy.egg-info/not-zip-safe +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/tpluspy.egg-info/requires.txt +0 -0
- {tpluspy-0.3.0 → tpluspy-0.3.2}/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.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: tpluspy: Client utilities for interacting with tplus
|
|
5
5
|
Home-page: https://github.com/tpluslabs/tpluspy
|
|
6
6
|
Author: TPlus Labs
|
|
@@ -264,7 +264,7 @@ async for trade in client.stream_finalized_trades():
|
|
|
264
264
|
# Other available streams:
|
|
265
265
|
# client.stream_orders() -> OrderEvent
|
|
266
266
|
# client.stream_all_trades() -> TradeEvent
|
|
267
|
-
# client.stream_klines(asset_id) ->
|
|
267
|
+
# client.stream_klines(asset_id) -> Timebar
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
See `examples/websocket_usage.py` for a runnable demonstration using `asyncio.gather` to run multiple streams concurrently.
|
|
@@ -193,7 +193,7 @@ async for trade in client.stream_finalized_trades():
|
|
|
193
193
|
# Other available streams:
|
|
194
194
|
# client.stream_orders() -> OrderEvent
|
|
195
195
|
# client.stream_all_trades() -> TradeEvent
|
|
196
|
-
# client.stream_klines(asset_id) ->
|
|
196
|
+
# client.stream_klines(asset_id) -> Timebar
|
|
197
197
|
```
|
|
198
198
|
|
|
199
199
|
See `examples/websocket_usage.py` for a runnable demonstration using `asyncio.gather` to run multiple streams concurrently.
|
|
@@ -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.3.
|
|
11
|
+
version="0.3.2",
|
|
12
12
|
description="""tpluspy: Client utilities for interacting with tplus""",
|
|
13
13
|
long_description=long_description,
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
@@ -120,6 +120,7 @@ def _depth(cli_ctx: CLIContext, asset_id: str):
|
|
|
120
120
|
@click.option("--page", type=int)
|
|
121
121
|
@click.option("--limit", type=int)
|
|
122
122
|
@click.option("--end-timestamp-ns", "end_timestamp_ns", type=int)
|
|
123
|
+
@click.option("--interval", help="Bucket width, e.g. 1, 5m, 4h, 1D, 1W or 1M (a month).")
|
|
123
124
|
@pass_cli_context
|
|
124
125
|
def _klines(
|
|
125
126
|
cli_ctx: CLIContext,
|
|
@@ -129,6 +130,7 @@ def _klines(
|
|
|
129
130
|
page: int | None,
|
|
130
131
|
limit: int | None,
|
|
131
132
|
end_timestamp_ns: int | None,
|
|
133
|
+
interval: str | None,
|
|
132
134
|
):
|
|
133
135
|
"""Get candlestick data for ASSET_ID."""
|
|
134
136
|
from tplus.model.asset_identifier import AssetIdentifier
|
|
@@ -136,7 +138,11 @@ def _klines(
|
|
|
136
138
|
client = cli_ctx.market_data_client()
|
|
137
139
|
klines = asyncio.run(
|
|
138
140
|
client.get_klines(
|
|
139
|
-
AssetIdentifier(asset_id),
|
|
141
|
+
AssetIdentifier(asset_id),
|
|
142
|
+
page=page,
|
|
143
|
+
limit=limit,
|
|
144
|
+
end_timestamp_ns=end_timestamp_ns,
|
|
145
|
+
interval=interval,
|
|
140
146
|
)
|
|
141
147
|
)
|
|
142
148
|
if output_format == "raw":
|
|
@@ -7,7 +7,13 @@ from tplus.client.auth import AuthenticatedClient
|
|
|
7
7
|
from tplus.client.base import page_params
|
|
8
8
|
from tplus.exceptions import NotFoundError
|
|
9
9
|
from tplus.model.asset_identifier import AssetIdentifier
|
|
10
|
-
from tplus.model.klines import
|
|
10
|
+
from tplus.model.klines import (
|
|
11
|
+
Interval,
|
|
12
|
+
KlinesPage,
|
|
13
|
+
Timebar,
|
|
14
|
+
parse_klines_page,
|
|
15
|
+
parse_timebars,
|
|
16
|
+
)
|
|
11
17
|
from tplus.model.order import OrderResponse, parse_orders
|
|
12
18
|
from tplus.model.orderbook import OrderBook, OrderBookDiff
|
|
13
19
|
from tplus.model.trades import (
|
|
@@ -25,17 +31,6 @@ from tplus.types import UserType
|
|
|
25
31
|
DEFAULT_BASE_URL = "http://localhost:8011"
|
|
26
32
|
|
|
27
33
|
|
|
28
|
-
def _pagination(page: int | None, limit: int | None) -> dict[str, Any]:
|
|
29
|
-
params: dict[str, Any] = {}
|
|
30
|
-
if page:
|
|
31
|
-
params["page"] = page
|
|
32
|
-
|
|
33
|
-
if limit:
|
|
34
|
-
params["limit"] = limit
|
|
35
|
-
|
|
36
|
-
return params
|
|
37
|
-
|
|
38
|
-
|
|
39
34
|
class MarketDataClient(AuthenticatedClient):
|
|
40
35
|
"""Klines, order-book depth, public trades and 24h tickers (REST + WS streams).
|
|
41
36
|
|
|
@@ -63,12 +58,19 @@ class MarketDataClient(AuthenticatedClient):
|
|
|
63
58
|
page: int | None = None,
|
|
64
59
|
limit: int | None = None,
|
|
65
60
|
end_timestamp_ns: int | None = None,
|
|
61
|
+
interval: Interval | str | None = None,
|
|
66
62
|
) -> KlinesPage:
|
|
67
|
-
"""A page of k-line (candlestick) data for `asset_id`, with pagination metadata.
|
|
68
|
-
params = _pagination(page, limit)
|
|
69
|
-
if end_timestamp_ns:
|
|
70
|
-
params["end_timestamp_ns"] = end_timestamp_ns
|
|
63
|
+
"""A page of k-line (candlestick) data for `asset_id`, with pagination metadata.
|
|
71
64
|
|
|
65
|
+
`interval` asks for wider candlesticks, e.g. `Interval.HOUR_4`. A raw string is
|
|
66
|
+
passed through, so the suffixed forms (`4h`, `1d`) also work.
|
|
67
|
+
"""
|
|
68
|
+
params = page_params(
|
|
69
|
+
page,
|
|
70
|
+
limit,
|
|
71
|
+
end_timestamp_ns=end_timestamp_ns,
|
|
72
|
+
interval=str(interval) if interval is not None else None,
|
|
73
|
+
)
|
|
72
74
|
response = await self._get(f"/klines/{asset_id}", params=params, requires_auth=False)
|
|
73
75
|
if not isinstance(response, dict | list):
|
|
74
76
|
raise ValueError(f"Invalid response from get_klines: {response}")
|
|
@@ -93,7 +95,7 @@ class MarketDataClient(AuthenticatedClient):
|
|
|
93
95
|
|
|
94
96
|
async def get_trades(self, page: int | None = None, limit: int | None = None) -> list[Trade]:
|
|
95
97
|
"""Confirmed trades across all markets."""
|
|
96
|
-
response = await self._get("/trades", params=
|
|
98
|
+
response = await self._get("/trades", params=page_params(page, limit), requires_auth=False)
|
|
97
99
|
if not isinstance(response, list):
|
|
98
100
|
raise ValueError(f"Invalid response from get_trades: {response}")
|
|
99
101
|
|
|
@@ -104,7 +106,7 @@ class MarketDataClient(AuthenticatedClient):
|
|
|
104
106
|
) -> list[Trade]:
|
|
105
107
|
"""Confirmed trades for `asset_id`."""
|
|
106
108
|
response = await self._get(
|
|
107
|
-
f"/trades/{asset_id}", params=
|
|
109
|
+
f"/trades/{asset_id}", params=page_params(page, limit), requires_auth=False
|
|
108
110
|
)
|
|
109
111
|
if not isinstance(response, list):
|
|
110
112
|
raise ValueError(f"Invalid response from get_trades_for_asset: {response}")
|
|
@@ -328,9 +330,9 @@ class MarketDataClient(AuthenticatedClient):
|
|
|
328
330
|
async for diff in self._stream_ws(path, lambda d: OrderBookDiff(**d), requires_auth=False):
|
|
329
331
|
yield diff
|
|
330
332
|
|
|
331
|
-
async def stream_klines(self, asset_id: AssetIdentifier) -> AsyncIterator[
|
|
333
|
+
async def stream_klines(self, asset_id: AssetIdentifier) -> AsyncIterator[Timebar]:
|
|
332
334
|
"""Candlestick (kline) updates for `asset_id`."""
|
|
333
335
|
async for kline in self._stream_ws(
|
|
334
|
-
f"/klines/diff/{asset_id}",
|
|
336
|
+
f"/klines/diff/{asset_id}", parse_timebars, requires_auth=False
|
|
335
337
|
):
|
|
336
338
|
yield kline
|
|
@@ -8,6 +8,7 @@ class OmsAdminClient(AuthenticatedClient):
|
|
|
8
8
|
solvency_verifier: str,
|
|
9
9
|
auto_reduce_enabled: bool,
|
|
10
10
|
interest_reservation_enabled: bool | None = None,
|
|
11
|
+
liquidation_monitor_enabled: bool | None = None,
|
|
11
12
|
):
|
|
12
13
|
config = {
|
|
13
14
|
"solvency_verifier": solvency_verifier,
|
|
@@ -15,6 +16,8 @@ class OmsAdminClient(AuthenticatedClient):
|
|
|
15
16
|
}
|
|
16
17
|
if interest_reservation_enabled is not None:
|
|
17
18
|
config["interest_reservation_enabled"] = interest_reservation_enabled
|
|
19
|
+
if liquidation_monitor_enabled is not None:
|
|
20
|
+
config["liquidation_monitor_enabled"] = liquidation_monitor_enabled
|
|
18
21
|
|
|
19
22
|
await self._post(
|
|
20
23
|
"admin/settings/modify",
|
|
@@ -770,6 +770,10 @@ class OrderBookClient(AuthenticatedClient):
|
|
|
770
770
|
content["order"].get("base_asset"), str
|
|
771
771
|
):
|
|
772
772
|
asset_id = content["order"]["base_asset"]
|
|
773
|
+
elif isinstance(content.get("request"), dict) and isinstance(
|
|
774
|
+
content["request"].get("base_asset"), str
|
|
775
|
+
):
|
|
776
|
+
asset_id = content["request"]["base_asset"]
|
|
773
777
|
if asset_id is None:
|
|
774
778
|
raise ValueError("WS control payload missing asset_id")
|
|
775
779
|
key = f"{response_variant}:{asset_id}:{expected_order_id}"
|
|
@@ -1123,7 +1127,10 @@ class OrderBookClient(AuthenticatedClient):
|
|
|
1123
1127
|
return parsed_data
|
|
1124
1128
|
|
|
1125
1129
|
async def set_mds_export(self, enabled: bool, user: UserType | None = None) -> dict[str, Any]:
|
|
1126
|
-
"""Set whether the user's trade history is exported to the market-data service.
|
|
1130
|
+
"""Set whether the user's trade history is exported to the market-data service.
|
|
1131
|
+
|
|
1132
|
+
Export is on by default; pass ``enabled=False`` to opt out.
|
|
1133
|
+
"""
|
|
1127
1134
|
public_key = self._validate_user_public_key(user=user)
|
|
1128
1135
|
return await self._put(
|
|
1129
1136
|
f"/account/{public_key}/mds-export",
|
|
@@ -1,17 +1,50 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
from decimal import Decimal
|
|
3
|
+
from enum import Enum
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
5
6
|
from pydantic import BaseModel
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
class
|
|
9
|
-
"""
|
|
9
|
+
class Interval(str, Enum):
|
|
10
|
+
"""Kline bucket width, mirroring `orderbook_messages::interval::Interval`.
|
|
11
|
+
|
|
12
|
+
Values are the canonical spellings the server round-trips; it also accepts the
|
|
13
|
+
suffixed forms (`5m`, `4h`, `1d`) on the wire. Only `1M` is case-sensitive: it is a
|
|
14
|
+
month, while `1m` is a minute.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
SEC_1 = "1S"
|
|
18
|
+
SEC_5 = "5S"
|
|
19
|
+
SEC_15 = "15S"
|
|
20
|
+
SEC_30 = "30S"
|
|
21
|
+
MIN_1 = "1"
|
|
22
|
+
MIN_3 = "3"
|
|
23
|
+
MIN_5 = "5"
|
|
24
|
+
MIN_15 = "15"
|
|
25
|
+
MIN_30 = "30"
|
|
26
|
+
HOUR_1 = "60"
|
|
27
|
+
HOUR_2 = "120"
|
|
28
|
+
HOUR_4 = "240"
|
|
29
|
+
HOUR_6 = "360"
|
|
30
|
+
HOUR_8 = "480"
|
|
31
|
+
HOUR_12 = "720"
|
|
32
|
+
DAY_1 = "1D"
|
|
33
|
+
DAY_3 = "3D"
|
|
34
|
+
WEEK_1 = "1W"
|
|
35
|
+
MONTH_1 = "1M"
|
|
36
|
+
|
|
37
|
+
def __str__(self) -> str:
|
|
38
|
+
return self.value
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Timebar(BaseModel):
|
|
42
|
+
"""One candlestick, mirroring `orderbook_messages::market_data::Timebar`."""
|
|
10
43
|
|
|
11
44
|
open: Decimal
|
|
12
|
-
high: Decimal
|
|
13
|
-
low: Decimal
|
|
14
45
|
close: Decimal
|
|
46
|
+
low: Decimal
|
|
47
|
+
high: Decimal
|
|
15
48
|
volume: Decimal
|
|
16
49
|
|
|
17
50
|
open_timestamp_ns: int
|
|
@@ -35,7 +68,7 @@ class KlineUpdate(BaseModel):
|
|
|
35
68
|
class KlinesPage(BaseModel):
|
|
36
69
|
"""One page of klines plus pagination metadata (the `Page<Timebar>` envelope)."""
|
|
37
70
|
|
|
38
|
-
items: list[
|
|
71
|
+
items: list[Timebar]
|
|
39
72
|
page: int
|
|
40
73
|
limit: int
|
|
41
74
|
total_pages: int
|
|
@@ -44,11 +77,11 @@ class KlinesPage(BaseModel):
|
|
|
44
77
|
next_page: int | None = None
|
|
45
78
|
|
|
46
79
|
|
|
47
|
-
def
|
|
48
|
-
"""Parses
|
|
80
|
+
def parse_timebars(data: list[dict[str, Any]]) -> list[Timebar]:
|
|
81
|
+
"""Parses a list of kline dictionaries into Timebar objects."""
|
|
49
82
|
try:
|
|
50
83
|
return [
|
|
51
|
-
|
|
84
|
+
Timebar(
|
|
52
85
|
open=Decimal(item["open"]),
|
|
53
86
|
high=Decimal(item["high"]),
|
|
54
87
|
low=Decimal(item["low"]),
|
|
@@ -60,14 +93,14 @@ def parse_kline_update(data: list[dict[str, Any]]) -> list[KlineUpdate]:
|
|
|
60
93
|
for item in data
|
|
61
94
|
]
|
|
62
95
|
except (KeyError, ValueError, TypeError) as e:
|
|
63
|
-
print(f"Error parsing
|
|
64
|
-
raise ValueError(f"Invalid
|
|
96
|
+
print(f"Error parsing Timebar: {e}. Data: {data}")
|
|
97
|
+
raise ValueError(f"Invalid Timebar data received: {data}") from e
|
|
65
98
|
|
|
66
99
|
|
|
67
100
|
def parse_klines_page(data: dict[str, Any] | list[dict[str, Any]]) -> KlinesPage:
|
|
68
101
|
"""Parse the `/klines` page envelope, tolerating a bare list from older servers."""
|
|
69
102
|
if isinstance(data, list):
|
|
70
|
-
items =
|
|
103
|
+
items = parse_timebars(data)
|
|
71
104
|
count = len(items)
|
|
72
105
|
return KlinesPage(
|
|
73
106
|
items=items,
|
|
@@ -79,7 +112,7 @@ def parse_klines_page(data: dict[str, Any] | list[dict[str, Any]]) -> KlinesPage
|
|
|
79
112
|
)
|
|
80
113
|
|
|
81
114
|
return KlinesPage(
|
|
82
|
-
items=
|
|
115
|
+
items=parse_timebars(data.get("items", [])),
|
|
83
116
|
page=int(data.get("page", 0)),
|
|
84
117
|
limit=int(data.get("limit", 0)),
|
|
85
118
|
total_pages=int(data.get("total_pages", 0)),
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
-
from pydantic import BaseModel, model_serializer
|
|
3
|
+
from pydantic import BaseModel, Field, model_serializer
|
|
4
4
|
|
|
5
5
|
from tplus.model.asset_identifier import AssetIdentifier
|
|
6
|
+
from tplus.model.multisig import AdditionalSigner
|
|
6
7
|
from tplus.model.order_id import UserOrderId
|
|
7
8
|
from tplus.model.order_trigger import TriggerAbove, TriggerBelow
|
|
8
9
|
|
|
@@ -44,6 +45,7 @@ class ReplaceOrderRequestPayload(BaseModel):
|
|
|
44
45
|
user_id: str # Added user_id field
|
|
45
46
|
signature: list[int] # Signature of the 'request' (ReplaceOrderDetails)
|
|
46
47
|
post_sign_timestamp: int
|
|
48
|
+
additional_signers: list[AdditionalSigner] = Field(default_factory=list)
|
|
47
49
|
|
|
48
50
|
@model_serializer
|
|
49
51
|
def serialize_model(self) -> dict[str, Any]:
|
|
@@ -55,4 +57,5 @@ class ReplaceOrderRequestPayload(BaseModel):
|
|
|
55
57
|
"signer": self.user_id, # Added user_id to serialization
|
|
56
58
|
"signature": self.signature,
|
|
57
59
|
"post_sign_timestamp": self.post_sign_timestamp,
|
|
60
|
+
"additional_signers": self.additional_signers,
|
|
58
61
|
}
|
|
@@ -56,15 +56,11 @@ def create_replace_order_ob_request_payload(
|
|
|
56
56
|
sign_payload_json.replace(" ", "").replace("\r", "").replace("\n", "")
|
|
57
57
|
)
|
|
58
58
|
signature, additional_signers = signer.signing_parts(compact_sign_payload_json)
|
|
59
|
-
if additional_signers:
|
|
60
|
-
raise ValueError(
|
|
61
|
-
"Order replacement does not support additional signers until its wire format "
|
|
62
|
-
"can identify replacement co-signatures."
|
|
63
|
-
)
|
|
64
59
|
|
|
65
60
|
return ReplaceOrderRequestPayload(
|
|
66
61
|
request=replace_details,
|
|
67
62
|
user_id=signer.public_key,
|
|
68
63
|
signature=signature,
|
|
69
64
|
post_sign_timestamp=time.time_ns(),
|
|
65
|
+
additional_signers=additional_signers,
|
|
70
66
|
)
|
|
@@ -22,12 +22,9 @@ def create_cancel_order_ob_request_payload(
|
|
|
22
22
|
compact_sign_payload_json = (
|
|
23
23
|
sign_payload_json.replace(" ", "").replace("\r", "").replace("\n", "")
|
|
24
24
|
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"Order cancellation does not support additional signers until its wire format "
|
|
29
|
-
"can identify cancellation co-signatures."
|
|
30
|
-
)
|
|
25
|
+
# Delegated cancels are authorized by the authenticated OMS session and account
|
|
26
|
+
# ownership. They do not reach the clearing engine, so no delegated signature is sent.
|
|
27
|
+
signature, _ = signer.signing_parts(compact_sign_payload_json)
|
|
31
28
|
|
|
32
29
|
return CancelOrderRequest(
|
|
33
30
|
cancel=cancel, signature=signature, post_sign_timestamp=time.time_ns()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tpluspy
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: tpluspy: Client utilities for interacting with tplus
|
|
5
5
|
Home-page: https://github.com/tpluslabs/tpluspy
|
|
6
6
|
Author: TPlus Labs
|
|
@@ -264,7 +264,7 @@ async for trade in client.stream_finalized_trades():
|
|
|
264
264
|
# Other available streams:
|
|
265
265
|
# client.stream_orders() -> OrderEvent
|
|
266
266
|
# client.stream_all_trades() -> TradeEvent
|
|
267
|
-
# client.stream_klines(asset_id) ->
|
|
267
|
+
# client.stream_klines(asset_id) -> Timebar
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
See `examples/websocket_usage.py` for a runnable demonstration using `asyncio.gather` to run multiple streams concurrently.
|
|
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
|