tpluspy 0.1.0a0__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.1.0a0/.github/workflows/publish.yml +29 -0
- tpluspy-0.1.0a0/.github/workflows/test.yml +50 -0
- tpluspy-0.1.0a0/.gitignore +183 -0
- tpluspy-0.1.0a0/.pre-commit-config.yaml +20 -0
- tpluspy-0.1.0a0/PKG-INFO +266 -0
- tpluspy-0.1.0a0/README.md +219 -0
- tpluspy-0.1.0a0/ape_console_extras.py +35 -0
- tpluspy-0.1.0a0/examples/decimals.py +29 -0
- tpluspy-0.1.0a0/examples/deposit.py +66 -0
- tpluspy-0.1.0a0/examples/rest_usage.py +175 -0
- tpluspy-0.1.0a0/examples/settlement.py +38 -0
- tpluspy-0.1.0a0/examples/two_users_trade.py +98 -0
- tpluspy-0.1.0a0/examples/vaults.py +24 -0
- tpluspy-0.1.0a0/examples/websocket_usage.py +144 -0
- tpluspy-0.1.0a0/pyproject.toml +127 -0
- tpluspy-0.1.0a0/setup.cfg +4 -0
- tpluspy-0.1.0a0/setup.py +72 -0
- tpluspy-0.1.0a0/tests/__init__.py +0 -0
- tpluspy-0.1.0a0/tests/client/__init__.py +0 -0
- tpluspy-0.1.0a0/tests/client/test_decimal.py +30 -0
- tpluspy-0.1.0a0/tests/evm/__init__.py +0 -0
- tpluspy-0.1.0a0/tests/evm/conftest.py +6 -0
- tpluspy-0.1.0a0/tests/evm/test_eip712.py +27 -0
- tpluspy-0.1.0a0/tests/model/__init__.py +0 -0
- tpluspy-0.1.0a0/tests/model/conftest.py +8 -0
- tpluspy-0.1.0a0/tests/model/test_asset_identifier.py +79 -0
- tpluspy-0.1.0a0/tests/model/test_settlement.py +92 -0
- tpluspy-0.1.0a0/tests/model/test_types.py +99 -0
- tpluspy-0.1.0a0/tests/model/test_withdrawal.py +29 -0
- tpluspy-0.1.0a0/tests/utils/__init__.py +0 -0
- tpluspy-0.1.0a0/tests/utils/test_asset_identifier.py +54 -0
- tpluspy-0.1.0a0/tests/utils/test_bytes32.py +28 -0
- tpluspy-0.1.0a0/tests/utils/test_decimals.py +70 -0
- tpluspy-0.1.0a0/tests/utils/test_hex.py +10 -0
- tpluspy-0.1.0a0/tests/utils/test_user.py +12 -0
- tpluspy-0.1.0a0/tplus/__init__.py +0 -0
- tpluspy-0.1.0a0/tplus/client/__init__.py +4 -0
- tpluspy-0.1.0a0/tplus/client/base.py +291 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/__init__.py +66 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/admin.py +30 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/assetregistry.py +37 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/base.py +5 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/decimal.py +47 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/deposit.py +19 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/settlement.py +84 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/vault.py +32 -0
- tpluspy-0.1.0a0/tplus/client/clearingengine/withdrawal.py +57 -0
- tpluspy-0.1.0a0/tplus/client/orderbook.py +395 -0
- tpluspy-0.1.0a0/tplus/constants.py +4 -0
- tpluspy-0.1.0a0/tplus/evm/__init__.py +1 -0
- tpluspy-0.1.0a0/tplus/evm/abi.py +21 -0
- tpluspy-0.1.0a0/tplus/evm/contracts.py +324 -0
- tpluspy-0.1.0a0/tplus/evm/eip712.py +16 -0
- tpluspy-0.1.0a0/tplus/evm/exceptions.py +4 -0
- tpluspy-0.1.0a0/tplus/evm/utils.py +0 -0
- tpluspy-0.1.0a0/tplus/logger.py +10 -0
- tpluspy-0.1.0a0/tplus/model/__init__.py +0 -0
- tpluspy-0.1.0a0/tplus/model/asset_identifier.py +124 -0
- tpluspy-0.1.0a0/tplus/model/cancel_order.py +20 -0
- tpluspy-0.1.0a0/tplus/model/klines.py +43 -0
- tpluspy-0.1.0a0/tplus/model/limit_order.py +74 -0
- tpluspy-0.1.0a0/tplus/model/market.py +17 -0
- tpluspy-0.1.0a0/tplus/model/market_order.py +60 -0
- tpluspy-0.1.0a0/tplus/model/order.py +219 -0
- tpluspy-0.1.0a0/tplus/model/order_trigger.py +25 -0
- tpluspy-0.1.0a0/tplus/model/orderbook.py +43 -0
- tpluspy-0.1.0a0/tplus/model/replace_order.py +49 -0
- tpluspy-0.1.0a0/tplus/model/settlement.py +200 -0
- tpluspy-0.1.0a0/tplus/model/signed_message.py +38 -0
- tpluspy-0.1.0a0/tplus/model/trades.py +158 -0
- tpluspy-0.1.0a0/tplus/model/types.py +87 -0
- tpluspy-0.1.0a0/tplus/model/withdrawal.py +57 -0
- tpluspy-0.1.0a0/tplus/py.typed +0 -0
- tpluspy-0.1.0a0/tplus/utils/__init__.py +0 -0
- tpluspy-0.1.0a0/tplus/utils/bytes32.py +23 -0
- tpluspy-0.1.0a0/tplus/utils/decimals.py +45 -0
- tpluspy-0.1.0a0/tplus/utils/hex.py +78 -0
- tpluspy-0.1.0a0/tplus/utils/limit_order.py +54 -0
- tpluspy-0.1.0a0/tplus/utils/market_order.py +50 -0
- tpluspy-0.1.0a0/tplus/utils/replace_order.py +58 -0
- tpluspy-0.1.0a0/tplus/utils/serializers.py +15 -0
- tpluspy-0.1.0a0/tplus/utils/signing.py +30 -0
- tpluspy-0.1.0a0/tplus/utils/user/__init__.py +10 -0
- tpluspy-0.1.0a0/tplus/utils/user/ed_keyfile.py +97 -0
- tpluspy-0.1.0a0/tplus/utils/user/manager.py +86 -0
- tpluspy-0.1.0a0/tplus/utils/user/model.py +45 -0
- tpluspy-0.1.0a0/tplus/utils/user/validate.py +9 -0
- tpluspy-0.1.0a0/tplus/version.py +16 -0
- tpluspy-0.1.0a0/tpluspy.egg-info/PKG-INFO +266 -0
- tpluspy-0.1.0a0/tpluspy.egg-info/SOURCES.txt +92 -0
- tpluspy-0.1.0a0/tpluspy.egg-info/dependency_links.txt +1 -0
- tpluspy-0.1.0a0/tpluspy.egg-info/not-zip-safe +1 -0
- tpluspy-0.1.0a0/tpluspy.egg-info/requires.txt +27 -0
- tpluspy-0.1.0a0/tpluspy.egg-info/top_level.txt +2 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Publish Python Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build-and-publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
|
|
13
|
+
- name: Set up Python
|
|
14
|
+
uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: 3.11
|
|
17
|
+
|
|
18
|
+
- name: Build package
|
|
19
|
+
run: |
|
|
20
|
+
python -m pip install --upgrade setuptools wheel
|
|
21
|
+
python setup.py sdist bdist_wheel
|
|
22
|
+
|
|
23
|
+
- name: Publish to GitHub Packages
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade twine
|
|
26
|
+
twine upload --repository-url https://upload.pypi.github.com/ dist/*
|
|
27
|
+
env:
|
|
28
|
+
TWINE_USERNAME: __token__
|
|
29
|
+
TWINE_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: tpluspy tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["**"]
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
# Cancel older, in-progress jobs from the same PR, same workflow.
|
|
11
|
+
# use run_id if the job is triggered by a push to ensure
|
|
12
|
+
# push-triggered jobs to not get canceled.
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 30
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout repository
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Setup Python
|
|
26
|
+
uses: actions/setup-python@v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.10"
|
|
29
|
+
|
|
30
|
+
- name: Install
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
pip install .[lint,test]
|
|
34
|
+
|
|
35
|
+
- name: Lint
|
|
36
|
+
run: ruff check .
|
|
37
|
+
|
|
38
|
+
- name: Format
|
|
39
|
+
run: ruff format --check .
|
|
40
|
+
|
|
41
|
+
# TODO: Handle type checking issues
|
|
42
|
+
# - name: Type Check
|
|
43
|
+
# run: mypy .
|
|
44
|
+
|
|
45
|
+
- name: Client Tests
|
|
46
|
+
run: pytest tests --ignore=tests/evm
|
|
47
|
+
|
|
48
|
+
# TODO: Uncomment when/if this actually matters (dependency-heavy)
|
|
49
|
+
# - name: EVM Tests
|
|
50
|
+
# run: pytest tests/evm
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Ape
|
|
2
|
+
.build
|
|
3
|
+
|
|
4
|
+
# Editor
|
|
5
|
+
.idea
|
|
6
|
+
|
|
7
|
+
# Setuptools SCM
|
|
8
|
+
tplus/version.py
|
|
9
|
+
|
|
10
|
+
# Byte-compiled / optimized / DLL files
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*$py.class
|
|
14
|
+
|
|
15
|
+
# C extensions
|
|
16
|
+
*.so
|
|
17
|
+
|
|
18
|
+
# Distribution / packaging
|
|
19
|
+
.Python
|
|
20
|
+
build/
|
|
21
|
+
develop-eggs/
|
|
22
|
+
dist/
|
|
23
|
+
downloads/
|
|
24
|
+
eggs/
|
|
25
|
+
.eggs/
|
|
26
|
+
lib/
|
|
27
|
+
lib64/
|
|
28
|
+
parts/
|
|
29
|
+
sdist/
|
|
30
|
+
var/
|
|
31
|
+
wheels/
|
|
32
|
+
share/python-wheels/
|
|
33
|
+
*.egg-info/
|
|
34
|
+
.installed.cfg
|
|
35
|
+
*.egg
|
|
36
|
+
MANIFEST
|
|
37
|
+
|
|
38
|
+
# PyInstaller
|
|
39
|
+
# Usually these files are written by a python script from a template
|
|
40
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
41
|
+
*.manifest
|
|
42
|
+
*.spec
|
|
43
|
+
|
|
44
|
+
# Installer logs
|
|
45
|
+
pip-log.txt
|
|
46
|
+
pip-delete-this-directory.txt
|
|
47
|
+
|
|
48
|
+
# Unit test / coverage reports
|
|
49
|
+
htmlcov/
|
|
50
|
+
.tox/
|
|
51
|
+
.nox/
|
|
52
|
+
.coverage
|
|
53
|
+
.coverage.*
|
|
54
|
+
.cache
|
|
55
|
+
nosetests.xml
|
|
56
|
+
coverage.xml
|
|
57
|
+
*.cover
|
|
58
|
+
*.py,cover
|
|
59
|
+
.hypothesis/
|
|
60
|
+
.pytest_cache/
|
|
61
|
+
cover/
|
|
62
|
+
|
|
63
|
+
# Translations
|
|
64
|
+
*.mo
|
|
65
|
+
*.pot
|
|
66
|
+
|
|
67
|
+
# Django stuff:
|
|
68
|
+
*.log
|
|
69
|
+
local_settings.py
|
|
70
|
+
db.sqlite3
|
|
71
|
+
db.sqlite3-journal
|
|
72
|
+
|
|
73
|
+
# Flask stuff:
|
|
74
|
+
instance/
|
|
75
|
+
.webassets-cache
|
|
76
|
+
|
|
77
|
+
# Scrapy stuff:
|
|
78
|
+
.scrapy
|
|
79
|
+
|
|
80
|
+
# Sphinx documentation
|
|
81
|
+
docs/_build/
|
|
82
|
+
|
|
83
|
+
# PyBuilder
|
|
84
|
+
.pybuilder/
|
|
85
|
+
target/
|
|
86
|
+
|
|
87
|
+
# Jupyter Notebook
|
|
88
|
+
.ipynb_checkpoints
|
|
89
|
+
|
|
90
|
+
# IPython
|
|
91
|
+
profile_default/
|
|
92
|
+
ipython_config.py
|
|
93
|
+
|
|
94
|
+
# pyenv
|
|
95
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
96
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
97
|
+
# .python-version
|
|
98
|
+
|
|
99
|
+
# pipenv
|
|
100
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
101
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
102
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
103
|
+
# install all needed dependencies.
|
|
104
|
+
#Pipfile.lock
|
|
105
|
+
|
|
106
|
+
# UV
|
|
107
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
108
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
109
|
+
# commonly ignored for libraries.
|
|
110
|
+
#uv.lock
|
|
111
|
+
|
|
112
|
+
# poetry
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
114
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
115
|
+
# commonly ignored for libraries.
|
|
116
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
117
|
+
#poetry.lock
|
|
118
|
+
|
|
119
|
+
# pdm
|
|
120
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
121
|
+
#pdm.lock
|
|
122
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
123
|
+
# in version control.
|
|
124
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
125
|
+
.pdm.toml
|
|
126
|
+
.pdm-python
|
|
127
|
+
.pdm-build/
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# SageMath parsed files
|
|
137
|
+
*.sage.py
|
|
138
|
+
|
|
139
|
+
# Environments
|
|
140
|
+
.env
|
|
141
|
+
.venv
|
|
142
|
+
env/
|
|
143
|
+
venv/
|
|
144
|
+
ENV/
|
|
145
|
+
env.bak/
|
|
146
|
+
venv.bak/
|
|
147
|
+
|
|
148
|
+
# Spyder project settings
|
|
149
|
+
.spyderproject
|
|
150
|
+
.spyproject
|
|
151
|
+
|
|
152
|
+
# Rope project settings
|
|
153
|
+
.ropeproject
|
|
154
|
+
|
|
155
|
+
# mkdocs documentation
|
|
156
|
+
/site
|
|
157
|
+
|
|
158
|
+
# mypy
|
|
159
|
+
.mypy_cache/
|
|
160
|
+
.dmypy.json
|
|
161
|
+
dmypy.json
|
|
162
|
+
|
|
163
|
+
# Pyre type checker
|
|
164
|
+
.pyre/
|
|
165
|
+
|
|
166
|
+
# pytype static type analyzer
|
|
167
|
+
.pytype/
|
|
168
|
+
|
|
169
|
+
# Cython debug symbols
|
|
170
|
+
cython_debug/
|
|
171
|
+
|
|
172
|
+
# PyCharm
|
|
173
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
174
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
175
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
176
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
177
|
+
#.idea/
|
|
178
|
+
|
|
179
|
+
# Ruff stuff:
|
|
180
|
+
.ruff_cache/
|
|
181
|
+
|
|
182
|
+
# PyPI configuration file
|
|
183
|
+
.pypirc
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-yaml
|
|
6
|
+
|
|
7
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
8
|
+
rev: v0.11.7
|
|
9
|
+
hooks:
|
|
10
|
+
- id: ruff
|
|
11
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
12
|
+
- id: ruff-format
|
|
13
|
+
|
|
14
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
15
|
+
rev: v1.15.0
|
|
16
|
+
hooks:
|
|
17
|
+
- id: mypy
|
|
18
|
+
|
|
19
|
+
default_language_version:
|
|
20
|
+
python: python3
|
tpluspy-0.1.0a0/PKG-INFO
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: tpluspy
|
|
3
|
+
Version: 0.1.0a0
|
|
4
|
+
Summary: tpluspy: Client utilities for interacting with tplus
|
|
5
|
+
Home-page: https://github.com/tpluslabs/tpluspy
|
|
6
|
+
Author: TPlus Labs
|
|
7
|
+
Author-email: admin@tlus.cx
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Keywords: ethereum
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Operating System :: POSIX
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Requires-Python: >=3.10,<4
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: cryptography>=44.0.1
|
|
24
|
+
Requires-Dist: ecdsa>=0.17
|
|
25
|
+
Requires-Dist: httpx>=0.20
|
|
26
|
+
Requires-Dist: pycryptodome>=3.17.1
|
|
27
|
+
Requires-Dist: pydantic<3,>=2.10.4
|
|
28
|
+
Requires-Dist: websockets<14,>=13.1
|
|
29
|
+
Requires-Dist: eth-pydantic-types<0.3,>=0.2.2
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest>=6.0; extra == "test"
|
|
32
|
+
Requires-Dist: pytest-timeout<3,>=2.2.0; extra == "test"
|
|
33
|
+
Requires-Dist: pytest-mock; extra == "test"
|
|
34
|
+
Provides-Extra: lint
|
|
35
|
+
Requires-Dist: mypy<2,>=1.15.0; extra == "lint"
|
|
36
|
+
Requires-Dist: ruff>=0.11.7; extra == "lint"
|
|
37
|
+
Requires-Dist: mypy<2,>=1.15.0; extra == "lint"
|
|
38
|
+
Provides-Extra: release
|
|
39
|
+
Requires-Dist: setuptools>=75.6.0; extra == "release"
|
|
40
|
+
Requires-Dist: wheel; extra == "release"
|
|
41
|
+
Requires-Dist: twine; extra == "release"
|
|
42
|
+
Provides-Extra: evm
|
|
43
|
+
Requires-Dist: ape-tokens; extra == "evm"
|
|
44
|
+
Requires-Dist: click; extra == "evm"
|
|
45
|
+
Requires-Dist: eip712; extra == "evm"
|
|
46
|
+
Requires-Dist: eth-ape<0.9,>=0.8.32; extra == "evm"
|
|
47
|
+
|
|
48
|
+
# TPlus Python Client Utilities
|
|
49
|
+
|
|
50
|
+
Python clients for interacting with tplus.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
To install, use either `pip` or `uv pip`:
|
|
55
|
+
|
|
56
|
+
```shell
|
|
57
|
+
uv pip install -e .
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage Example
|
|
61
|
+
|
|
62
|
+
### REST and WebSocket Client (`tplus.client`)
|
|
63
|
+
|
|
64
|
+
The `tpluspy` library also provides an asynchronous client (`OrderBookClient`) for interacting with the `tplus-core` REST API and WebSocket streams.
|
|
65
|
+
|
|
66
|
+
#### Initialization
|
|
67
|
+
|
|
68
|
+
To use the client, first initialize it with a `User` object (for signing requests) and the base URL of your `tplus-core` instance:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import asyncio
|
|
72
|
+
from tplus.client import OrderBookClient
|
|
73
|
+
from tplus.utils.user import User
|
|
74
|
+
|
|
75
|
+
API_BASE_URL = "http://127.0.0.1:8000" # Replace with your API URL
|
|
76
|
+
user = User()
|
|
77
|
+
|
|
78
|
+
async def run_client():
|
|
79
|
+
# Use async context manager for automatic cleanup
|
|
80
|
+
async with OrderBookClient(user=user, base_url=API_BASE_URL) as client:
|
|
81
|
+
print("Client initialized.")
|
|
82
|
+
# ... use client methods ...
|
|
83
|
+
|
|
84
|
+
asyncio.run(run_client())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### REST API Usage
|
|
88
|
+
|
|
89
|
+
The client offers async methods for common REST endpoints:
|
|
90
|
+
|
|
91
|
+
**Fetching Data:**
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
# Get Order Book Snapshot for asset index 200
|
|
95
|
+
from tplus.model.asset_identifier import AssetIdentifier
|
|
96
|
+
|
|
97
|
+
example_asset = AssetIdentifier(Index=200)
|
|
98
|
+
orderbook = await client.get_orderbook_snapshot(example_asset)
|
|
99
|
+
print(f"Snapshot Sequence: {orderbook.sequence_number}")
|
|
100
|
+
|
|
101
|
+
# Get Klines for an asset
|
|
102
|
+
klines = await client.get_klines(example_asset)
|
|
103
|
+
print(f"Klines: {klines}")
|
|
104
|
+
|
|
105
|
+
# Get Market Details for an asset
|
|
106
|
+
market_details = await client.get_market(example_asset)
|
|
107
|
+
print(f"Market Details: Price Decimals={market_details.book_price_decimals}, Quantity Decimals={market_details.book_quantity_decimals}")
|
|
108
|
+
|
|
109
|
+
# Get orders for the user
|
|
110
|
+
user_orders, _ = await client.get_user_orders()
|
|
111
|
+
print(f"User Orders: {user_orders}")
|
|
112
|
+
|
|
113
|
+
# Get trades for the user and asset
|
|
114
|
+
user_asset_trades = await client.get_user_trades_for_asset(example_asset)
|
|
115
|
+
print(f"User Asset Trades: {user_asset_trades}")
|
|
116
|
+
|
|
117
|
+
# Get user inventory
|
|
118
|
+
inventory = await client.get_user_inventory()
|
|
119
|
+
print(f"Inventory: {inventory}")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Creating Orders:**
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
# Ensure example_asset is defined (e.g., from "Fetching Data" section)
|
|
126
|
+
from tplus.model.asset_identifier import AssetIdentifier
|
|
127
|
+
example_asset = AssetIdentifier(200)
|
|
128
|
+
|
|
129
|
+
# Create a Market for an asset (idempotent)
|
|
130
|
+
market_creation_response = await client.create_market(asset_id=example_asset)
|
|
131
|
+
print(f"Market Creation Response: {market_creation_response}")
|
|
132
|
+
|
|
133
|
+
# Create a Market Order for a specific asset
|
|
134
|
+
market_response = await client.create_market_order(
|
|
135
|
+
asset_id=example_asset,
|
|
136
|
+
quantity=10, # integer quantity
|
|
137
|
+
side="Buy",
|
|
138
|
+
fill_or_kill=False,
|
|
139
|
+
)
|
|
140
|
+
print(f"Market Order Response: {market_response}")
|
|
141
|
+
|
|
142
|
+
# Create a Limit Order for a specific asset
|
|
143
|
+
# Good-Till-Cancelled limit order
|
|
144
|
+
from tplus.model.limit_order import GTC
|
|
145
|
+
limit_response = await client.create_limit_order(
|
|
146
|
+
asset_id=example_asset,
|
|
147
|
+
quantity=5,
|
|
148
|
+
price=1_000,
|
|
149
|
+
side="Sell",
|
|
150
|
+
time_in_force=GTC(),
|
|
151
|
+
)
|
|
152
|
+
print(f"Limit Order Response: {limit_response}")
|
|
153
|
+
|
|
154
|
+
# Cancel an Order
|
|
155
|
+
# Order ID should be obtained from an order creation response.
|
|
156
|
+
order_id_to_cancel = "actual-order-id-from-api" # Replace with a real order ID
|
|
157
|
+
cancel_response = await client.cancel_order(
|
|
158
|
+
order_id=order_id_to_cancel,
|
|
159
|
+
asset_id=example_asset
|
|
160
|
+
)
|
|
161
|
+
print(f"Cancel Order Response: {cancel_response}")
|
|
162
|
+
|
|
163
|
+
# Replace an Order
|
|
164
|
+
# Original Order ID should be from an existing, open order.
|
|
165
|
+
original_order_id_to_replace = "actual-original-order-id" # Replace with a real order ID
|
|
166
|
+
replace_response = await client.replace_order(
|
|
167
|
+
original_order_id=original_order_id_to_replace,
|
|
168
|
+
asset_id=example_asset,
|
|
169
|
+
new_quantity=6, # Optional: New integer quantity
|
|
170
|
+
new_price=1050 # Optional: New integer price
|
|
171
|
+
)
|
|
172
|
+
print(f"Replace Order Response: {replace_response}")
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
See `examples/rest_usage.py` for a runnable demonstration.
|
|
176
|
+
|
|
177
|
+
#### WebSocket Streaming
|
|
178
|
+
|
|
179
|
+
The client provides async iterators to stream real-time data:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from tplus.model.asset_identifier import AssetIdentifier
|
|
183
|
+
from tplus.model.orderbook import OrderBookDiff
|
|
184
|
+
from tplus.model.trades import Trade
|
|
185
|
+
|
|
186
|
+
example_asset = AssetIdentifier(200)
|
|
187
|
+
|
|
188
|
+
# Stream Order Book Diffs
|
|
189
|
+
async for diff_update in client.stream_depth(example_asset):
|
|
190
|
+
if isinstance(diff_update, OrderBookDiff):
|
|
191
|
+
print(f"[Depth] Seq={diff_update.sequence_number}, Asks={len(diff_update.asks)}, Bids={len(diff_update.bids)}")
|
|
192
|
+
# Add logic to handle the update, e.g., update a local order book
|
|
193
|
+
|
|
194
|
+
# Stream Finalized Trades
|
|
195
|
+
async for trade in client.stream_finalized_trades():
|
|
196
|
+
if isinstance(trade, Trade):
|
|
197
|
+
print(f"[Trade] ID: {trade.trade_id}, Price: {trade.price}, Qty: {trade.quantity}")
|
|
198
|
+
# Add logic to handle the trade
|
|
199
|
+
|
|
200
|
+
# Other available streams:
|
|
201
|
+
# client.stream_orders() -> OrderEvent
|
|
202
|
+
# client.stream_all_trades() -> TradeEvent
|
|
203
|
+
# client.stream_klines(asset_id) -> KlineUpdate
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
See `examples/websocket_usage.py` for a runnable demonstration using `asyncio.gather` to run multiple streams concurrently.
|
|
207
|
+
|
|
208
|
+
### Contracts
|
|
209
|
+
|
|
210
|
+
To interact with the contracts or sign T+ specific EIP-712 messages, ensure you have installed the `evm` extra:
|
|
211
|
+
|
|
212
|
+
```shell
|
|
213
|
+
pip install tpluspy[evm]
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Use the `tplusp.contracts` module to read data from t+ contracts.
|
|
217
|
+
For example, launch a Sepolia-connected Ape console:
|
|
218
|
+
|
|
219
|
+
```shell
|
|
220
|
+
ape console --network ethereum:sepolia:alchemy
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Note**: You can use any provider you want or a RPC directly, it doesn't have to be Alchemy.
|
|
224
|
+
|
|
225
|
+
Then, once in the console, you will already have access to contracts that you can call methods on:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
In [1]: registry.getAssets()
|
|
229
|
+
Out[1]: [getAssets_return(assetAddress=HexBytes('0x000000000000000000000000f08a50178dfcde18524640ea6618a1f965821715'), chainId=11155111, maxDeposits=100)]
|
|
230
|
+
In [2]: registry.admin()
|
|
231
|
+
Out[2]: '0x467a95fC5359edE5d5dDc4f10A1F4B680694858E'
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
#### EIP-712
|
|
235
|
+
|
|
236
|
+
Sign EIP-712 messages, such as settlements, using the `eip712` library.
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
from ape import accounts, convert, chain
|
|
240
|
+
from tplus.evm.eip712 import Order
|
|
241
|
+
from tplus.evm.contracts import vault
|
|
242
|
+
from tplus.utils.user import UserManager
|
|
243
|
+
|
|
244
|
+
# Load your Ethereum account for t+.
|
|
245
|
+
tplus_user = accounts.load("tplus-account")
|
|
246
|
+
|
|
247
|
+
# Load your t+ user (public key).
|
|
248
|
+
user_id = UserManager.load("my_user").public_key
|
|
249
|
+
|
|
250
|
+
# Get the nonce from t+ or the contracts directly.
|
|
251
|
+
nonce = vault.getDepositNonce(tplus_user)
|
|
252
|
+
|
|
253
|
+
order = Order(
|
|
254
|
+
tokenOut="0x62622E77D1349Face943C6e7D5c01C61465FE1dc",
|
|
255
|
+
amountOut=convert("1 ether", int),
|
|
256
|
+
tokenIn="0x58372ab62269A52fA636aD7F200d93999595DCAF",
|
|
257
|
+
amountIn=convert("1 ether", int),
|
|
258
|
+
userId=user_id,
|
|
259
|
+
nonce=nonce,
|
|
260
|
+
validUntil=chain.pending_timestamp,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Use this signature for the settlement.
|
|
264
|
+
signature = tplus_user.sign_message(order).encode_rsv()
|
|
265
|
+
print(signature)
|
|
266
|
+
```
|