x10-python-trading-starknet 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. x10_python_trading_starknet-0.0.1/LICENSE +21 -0
  2. x10_python_trading_starknet-0.0.1/PKG-INFO +413 -0
  3. x10_python_trading_starknet-0.0.1/README.md +385 -0
  4. x10_python_trading_starknet-0.0.1/pyproject.toml +64 -0
  5. x10_python_trading_starknet-0.0.1/x10/__init__.py +0 -0
  6. x10_python_trading_starknet-0.0.1/x10/config.py +13 -0
  7. x10_python_trading_starknet-0.0.1/x10/errors.py +2 -0
  8. x10_python_trading_starknet-0.0.1/x10/perpetual/__init__.py +0 -0
  9. x10_python_trading_starknet-0.0.1/x10/perpetual/accounts.py +86 -0
  10. x10_python_trading_starknet-0.0.1/x10/perpetual/amounts.py +55 -0
  11. x10_python_trading_starknet-0.0.1/x10/perpetual/assets.py +71 -0
  12. x10_python_trading_starknet-0.0.1/x10/perpetual/balances.py +15 -0
  13. x10_python_trading_starknet-0.0.1/x10/perpetual/candles.py +18 -0
  14. x10_python_trading_starknet-0.0.1/x10/perpetual/configuration.py +86 -0
  15. x10_python_trading_starknet-0.0.1/x10/perpetual/fees.py +16 -0
  16. x10_python_trading_starknet-0.0.1/x10/perpetual/funding_rates.py +11 -0
  17. x10_python_trading_starknet-0.0.1/x10/perpetual/markets.py +125 -0
  18. x10_python_trading_starknet-0.0.1/x10/perpetual/order_object.py +201 -0
  19. x10_python_trading_starknet-0.0.1/x10/perpetual/orderbook.py +211 -0
  20. x10_python_trading_starknet-0.0.1/x10/perpetual/orderbooks.py +17 -0
  21. x10_python_trading_starknet-0.0.1/x10/perpetual/orders.py +179 -0
  22. x10_python_trading_starknet-0.0.1/x10/perpetual/positions.py +51 -0
  23. x10_python_trading_starknet-0.0.1/x10/perpetual/simple_client/simple_trading_client.py +216 -0
  24. x10_python_trading_starknet-0.0.1/x10/perpetual/stream_client/__init__.py +3 -0
  25. x10_python_trading_starknet-0.0.1/x10/perpetual/stream_client/perpetual_stream_connection.py +108 -0
  26. x10_python_trading_starknet-0.0.1/x10/perpetual/stream_client/stream_client.py +83 -0
  27. x10_python_trading_starknet-0.0.1/x10/perpetual/trades.py +38 -0
  28. x10_python_trading_starknet-0.0.1/x10/perpetual/trading_client/__init__.py +3 -0
  29. x10_python_trading_starknet-0.0.1/x10/perpetual/trading_client/account_module.py +209 -0
  30. x10_python_trading_starknet-0.0.1/x10/perpetual/trading_client/base_module.py +58 -0
  31. x10_python_trading_starknet-0.0.1/x10/perpetual/trading_client/info_module.py +13 -0
  32. x10_python_trading_starknet-0.0.1/x10/perpetual/trading_client/markets_information_module.py +76 -0
  33. x10_python_trading_starknet-0.0.1/x10/perpetual/trading_client/order_management_module.py +81 -0
  34. x10_python_trading_starknet-0.0.1/x10/perpetual/trading_client/testnet_module.py +68 -0
  35. x10_python_trading_starknet-0.0.1/x10/perpetual/trading_client/trading_client.py +123 -0
  36. x10_python_trading_starknet-0.0.1/x10/perpetual/transfer_object.py +80 -0
  37. x10_python_trading_starknet-0.0.1/x10/perpetual/transfers.py +39 -0
  38. x10_python_trading_starknet-0.0.1/x10/perpetual/user_client/__init__.py +0 -0
  39. x10_python_trading_starknet-0.0.1/x10/perpetual/user_client/l1_signing.py +0 -0
  40. x10_python_trading_starknet-0.0.1/x10/perpetual/user_client/onboarding.py +231 -0
  41. x10_python_trading_starknet-0.0.1/x10/perpetual/user_client/user_client.py +199 -0
  42. x10_python_trading_starknet-0.0.1/x10/perpetual/withdrawal_object.py +29 -0
  43. x10_python_trading_starknet-0.0.1/x10/perpetual/withdrawals.py +29 -0
  44. x10_python_trading_starknet-0.0.1/x10/utils/__init__.py +2 -0
  45. x10_python_trading_starknet-0.0.1/x10/utils/date.py +11 -0
  46. x10_python_trading_starknet-0.0.1/x10/utils/http.py +239 -0
  47. x10_python_trading_starknet-0.0.1/x10/utils/log.py +5 -0
  48. x10_python_trading_starknet-0.0.1/x10/utils/model.py +66 -0
  49. x10_python_trading_starknet-0.0.1/x10/utils/nonce.py +11 -0
  50. x10_python_trading_starknet-0.0.1/x10/utils/string.py +10 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024, X10
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,413 @@
1
+ Metadata-Version: 2.1
2
+ Name: x10-python-trading-starknet
3
+ Version: 0.0.1
4
+ Summary: Python client for X10 API
5
+ Home-page: https://github.com/x10xchange/python_sdk
6
+ Author: X10
7
+ Author-email: tech@ex10.org
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Requires-Dist: aiohttp (>=3.10.11)
18
+ Requires-Dist: eth-account (>=0.12.0)
19
+ Requires-Dist: fast-stark-crypto (==0.3.4a0)
20
+ Requires-Dist: pydantic (>=2.9.0)
21
+ Requires-Dist: pyyaml (>=6.0.1)
22
+ Requires-Dist: sortedcontainers (>=2.4.0)
23
+ Requires-Dist: websockets (>=12.0,<14.0)
24
+ Project-URL: Documentation, https://api.docs.extended.exchange/
25
+ Project-URL: Repository, https://github.com/x10xchange/python_sdk
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Extended Python SDK
29
+
30
+ Python client for [Extended API](https://api.docs.extended.exchange/).
31
+
32
+ Minimum Python version required to use this library is `3.10` (you can use [pyenv](https://github.com/pyenv/pyenv) to manage your Python versions easily).
33
+
34
+ ## Installation
35
+
36
+ ```shell
37
+ pip install x10-python-trading
38
+ ```
39
+
40
+ Our SDK makes use of a [Rust Library](https://github.com/x10xchange/stark-crypto-wrapper) to accelerate signing and hashing of stark components. Currently this library supports the following environments
41
+
42
+ | | 3.9 | 3.10 | 3.11 | 3.12 |
43
+ | --------------------- | :---: | :---: | :---: | :---: |
44
+ | linux (glibc) - x86 | ✅ | ✅ | ✅ | ✅ |
45
+ | linux (musl) - x86 | ✅ | ✅ | ✅ | ✅ |
46
+ | linux (glibc) - arm64 | ✅ | ✅ | ✅ | ✅ |
47
+ | linux (musl) - arm64 | ✅ | ✅ | ✅ | ✅ |
48
+ | OSX - arm64 | ✅ | ✅ | ✅ | ✅ |
49
+ | windows - x86 | ✅ | ✅ | ✅ | ✅ |
50
+ | windows - arm64 | ⚠️ | ⚠️ | ⚠️ | ⚠️ |
51
+
52
+
53
+
54
+ ## TLDR:
55
+
56
+ Register at [Extended Testnet](https://testnet.extended.exchange/) by connecting a supported Ethereum Wallet.
57
+
58
+ Navigate to [Api Management](https://testnet.extended.exchange/api-management)
59
+ 1. Generate an API key
60
+ 2. Show API details (You will need these details to initialise a trading client)
61
+
62
+ Instantiate a Trading Account
63
+
64
+ ```python
65
+ from x10.perpetual.accounts import StarkPerpetualAccount
66
+ api_key:str = "<api>" #from api-management
67
+ public_key:str = "<public>" #from api-management
68
+ private_key:str = "<private>" #from api-management
69
+ vault:int = <vault> #from api-management
70
+
71
+ stark_account = StarkPerpetualAccount(
72
+ vault=vault,
73
+ private_key=private_key,
74
+ public_key=public_key,
75
+ api_key=api_key,
76
+ )
77
+ ```
78
+
79
+ Instantiate a Trading Client
80
+ ```python
81
+ from x10.perpetual.accounts import StarkPerpetualAccount
82
+ from x10.perpetual.configuration import TESTNET_CONFIG
83
+ from x10.perpetual.orders import OrderSide
84
+ from x10.perpetual.trading_client import PerpetualTradingClient
85
+
86
+ trading_client = PerpetualTradingClient.create(TESTNET_CONFIG, stark_account)
87
+ placed_order = await trading_client.place_order(
88
+ market_name="BTC-USD",
89
+ amount_of_synthetic=Decimal("1"),
90
+ price=Decimal("63000.1"),
91
+ side=OrderSide.SELL,
92
+ )
93
+ await trading_client.orders.cancel_order(order_id=placed_order.id)
94
+ print(placed_order)
95
+ ```
96
+
97
+ for more information see [placing an order example](examples/placed_order_example_simple.py).
98
+
99
+ There is also a skeleton implementation of a [blocking client](examples/simple_client_example.py).
100
+
101
+ ## Modules
102
+
103
+ The SDK currently provides functionality across three main modules
104
+
105
+ ### Order Management Module
106
+ The order module is accessed using the `orders` property of the trading client
107
+ ```python
108
+ trading_client.orders
109
+ ```
110
+ TODO
111
+
112
+ ### Account Module
113
+ The account module is accessed using the account property of the trading client
114
+ ```python
115
+ trading_client.account
116
+ ```
117
+
118
+ it exposes functionality related to managing an active trading account
119
+
120
+ #### `get_balance`
121
+ Fetches the balance of the user's account.
122
+
123
+ ```python
124
+ logger = logging.getLogger("demo_logger")
125
+ balance = await trading_client.account.get_balance()
126
+ logger.info("Balance: %s", balance.to_pretty_json())
127
+ ```
128
+
129
+ #### `get_positions`
130
+ Fetches the current positions of the user's account. It can filter the positions based on market names and position side.
131
+
132
+ ```python
133
+ logger = logging.getLogger("demo_logger")
134
+ positions = await trading_client.account.get_positions()
135
+ logger.info("Positions: %s", positions.to_pretty_json())
136
+ ```
137
+
138
+ returns a list of
139
+ ```python
140
+ class PositionModel(X10BaseModel):
141
+ id: int
142
+ account_id: int
143
+ market: str
144
+ side: PositionSide
145
+ leverage: Decimal
146
+ size: Decimal
147
+ value: Decimal
148
+ open_price: Decimal
149
+ mark_price: Decimal
150
+ liquidation_price: Optional[Decimal] = None
151
+ unrealised_pnl: Decimal
152
+ realised_pnl: Decimal
153
+ tp_price: Optional[Decimal] = None
154
+ sl_price: Optional[Decimal] = None
155
+ adl: Optional[int] = None
156
+ created_at: int
157
+ updated_at: int
158
+ ```
159
+
160
+ #### `get_positions_history`
161
+ Fetches the historical positions of the user's account. It can filter the positions based on market names and position side.
162
+
163
+ ```python
164
+ logger = logging.getLogger("demo_logger")
165
+ positions = await trading_client.account.get_positions_history()
166
+ logger.info("Positions: %s", positions.to_pretty_json())
167
+ ```
168
+
169
+ returns a list of
170
+ ```python
171
+ class PositionHistoryModel(X10BaseModel):
172
+ id: int
173
+ account_id: int
174
+ market: str
175
+ side: PositionSide
176
+ leverage: Decimal
177
+ size: Decimal
178
+ open_price: Decimal
179
+ exit_type: Optional[ExitType]
180
+ exit_price: Optional[Decimal]
181
+ realised_pnl: Decimal
182
+ created_time: int
183
+ closed_time: Optional[int]
184
+ ```
185
+
186
+ #### `get_open_orders`
187
+ Fetches the open orders of the user's account. It can filter the orders based on market names, order type, and order side.
188
+
189
+ ```python
190
+ open_orders = await trading_client.account.get_open_orders()
191
+ await trading_client.orders.mass_cancel(order_ids=[order.id for order in open_orders.data])
192
+ ```
193
+ returns a list of
194
+ ```python
195
+ class OpenOrderModel(X10BaseModel):
196
+ id: int
197
+ account_id: int
198
+ external_id: str
199
+ market: str
200
+ type: OrderType
201
+ side: OrderSide
202
+ status: OrderStatus
203
+ status_reason: Optional[OrderStatusReason] = None
204
+ price: Decimal
205
+ average_price: Optional[Decimal] = None
206
+ qty: Decimal
207
+ filled_qty: Optional[Decimal] = None
208
+ reduce_only: bool
209
+ post_only: bool
210
+ created_time: int
211
+ updated_time: int
212
+ expiry_time: Optional[int] = None
213
+ ```
214
+
215
+ #### `get_orders_history`
216
+ Fetches the historical orders of the user's account. It can filter the orders based on market names, order type, and order side
217
+
218
+ ```python
219
+ market_names: Optional[List[str]] = None, #parameter to filter by market
220
+ order_type: Optional[OrderType] = None, #parameter to filter by order type (IOC, GTT etc)
221
+ order_side: Optional[OrderSide] = None, #parameter to filter by side (BUY/SELL)
222
+ cursor: Optional[int] = None, #pagination cursor
223
+ limit: Optional[int] = None, #limit the number of returned orders per page
224
+ ```
225
+
226
+ ```python
227
+ open_orders = await trading_client.account.get_orders_history(
228
+ market_names=["BTC-USD", "SOL-USD"],
229
+ order_side=OrderSide.BUY
230
+ )
231
+ ```
232
+ returns a list of `OpenOrderModel`
233
+
234
+ #### `get_trades`
235
+ Fetches the trades of the user's account. It can filter the trades based on market names, trade side, and trade type.
236
+
237
+ ```python
238
+ ```
239
+
240
+ #### `get_fees`
241
+ Fetches the trading fees for the specified markets.
242
+
243
+ ```python
244
+ pass
245
+ ```
246
+
247
+ #### `get_leverage`
248
+ Fetches the leverage for the specified markets.
249
+
250
+ ```python
251
+ leverage = await trading_client.account.get_leverage(market_names=list("BTC-USD"))
252
+ print(leverage)
253
+ ```
254
+
255
+ returns a list of
256
+ ```python
257
+ class AccountLeverage(X10BaseModel):
258
+ market: str
259
+ leverage: Decimal
260
+ ```
261
+
262
+ #### `update_leverage`
263
+ Updates the leverage for a specific market.
264
+
265
+ ```python
266
+ await trading_client.account.update_leverage(market_name="BTC-USD", leverage=Decimal("20.0"))
267
+ ```
268
+
269
+ ### Markets Info Module
270
+ The markets module is accessed using the `markets_info` property of the trading client
271
+ ```python
272
+ trading_client.markets_info
273
+ ```
274
+ TODO
275
+
276
+ ## SDK Environment configurations (Since version 0.3.0)
277
+
278
+ The SDK is controlled by an `EndpointConfiguration` object passed to the various methods and clients, several helpful instances are defined in [configuration.py](x10/perpetual/configuration.py)
279
+
280
+ ### `MAINNET_CONFIG` vs `MAINNET_CONFIG_LEGACY_SIGNING_DOMAIN`
281
+ If you previously onboarded to our mainnet environment on `app.x10.exchange`, you should use the `MAINNET_CONFIG_LEGACY_SIGNING_DOMAIN` configuration bundle, as this will allow you to regenerate the same l2 keys as were created by our mainnet environment that was running on a legacy domain.
282
+
283
+ All new accounts should use the `MAINNET_CONFIG` configuration bundle.
284
+
285
+ ## OnBoarding via SDK (Since Version 0.3.0)
286
+
287
+ To onboard to the Extended Exchange, the `UserClient` defined in [user_client.py](x10/perpetual/user_client/user_client.py) provides a way to use an Ethereum account to onboard onto the Extended Exchange.
288
+
289
+ ### TLDR - Check out: [onboarding_example.py](examples/onboarding_example.py)
290
+
291
+ ### `onboard(referral_code: Optional[str] = None) -> OnBoardedAccount`
292
+ This method handles the onboarding process of a user. It generates an L2 key pair from the user's L1 Ethereum account, creates an onboarding payload, and sends it to the onboarding endpoint. Upon successful onboarding, it returns an `OnBoardedAccount` object containing the default account and the L2 key pair.
293
+
294
+ ### `onboard_subaccount(account_index: int, description: str | None = None) -> OnBoardedAccount`
295
+ This method onboards a subaccount associated with the user's main account. It allows you to specify an `account_index` and an optional description. If a subaccount with the given index already exists, it retrieves and returns that subaccount. Otherwise, it creates a new subaccount and returns an `OnBoardedAccount` object with the subaccount details and the associated L2 key pair.
296
+
297
+ ### `get_accounts() -> List[OnBoardedAccount]`
298
+ This method retrieves all the accounts associated with the user. It returns a list of `OnBoardedAccount` objects, each containing the account details and corresponding L2 key pair.
299
+
300
+ ### `create_account_api_key(account: AccountModel, description: str | None) -> str`
301
+ This method generates an API key for a specified account. You can provide an optional description for the API key. It returns the newly created API key as a string.
302
+
303
+ ### `perform_l1_withdrawal() -> str`
304
+ This method initiates a withdrawal from Layer 2 (L2) to Layer 1 (L1) using the user's Ethereum account. It calls the underlying contract function to perform the withdrawal and returns a string, typically a transaction hash or status.
305
+
306
+ ### `available_l1_withdrawal_balance() -> Decimal`
307
+ This method retrieves the available balance for L1 withdrawals. It calls the underlying contract function to fetch the withdrawal balance and returns the balance as a `Decimal` value.
308
+
309
+ ### Process of Obtaining a Stark Key Pair from an Ethereum Account
310
+
311
+ The process of obtaining a Stark key pair from an Ethereum account is a cryptographic procedure that involves generating a private and public key pair used in the StarkWare ecosystem. This process leverages the Ethereum account to create a deterministic Stark key pair that can be used for operations on StarkWare-based systems such as StarkEx and StarkNet
312
+
313
+ #### 1. Context and Purpose
314
+
315
+ StarkWare-based systems require their own cryptographic keys (Stark keys) separate from Ethereum keys. However, to maintain a consistent user experience, StarkWare allows users to derive these keys deterministically from their existing Ethereum accounts. The process of obtaining a Stark key pair from an Ethereum account involves generating a signing message that the Ethereum account can sign, and then using that signature to derive the Stark private key.
316
+
317
+ #### 2. Generating the Signing Structure
318
+
319
+ The first step in the process is to generate a signing structure that will be signed by the Ethereum account. This structure is constructed using the EIP-712 standard, which allows for typed data to be signed in a structured way on Ethereum.
320
+
321
+ ##### a. Define the Signing Structure
322
+
323
+ The message to be signed includes:
324
+ 1. account index,
325
+ 2. the Ethereum wallet address,
326
+ 3. and whether the terms of service (TOS) are accepted.
327
+
328
+ in the function `get_key_derivation_struct_to_sign`, the signing structure is constructed as follows:
329
+
330
+ ```python
331
+ def get_key_derivation_struct_to_sign(account_index: int, address: str, signing_domain: str) -> SignableMessage:
332
+ primary_type = "AccountCreation"
333
+ domain = {"name": signing_domain}
334
+ message = {
335
+ "accountIndex": account_index,
336
+ "wallet": address,
337
+ "tosAccepted": True,
338
+ }
339
+ types = {
340
+ "EIP712Domain": [
341
+ {"name": "name", "type": "string"},
342
+ ],
343
+ "AccountCreation": [
344
+ {"name": "accountIndex", "type": "int8"},
345
+ {"name": "wallet", "type": "address"},
346
+ {"name": "tosAccepted", "type": "bool"},
347
+ ],
348
+ }
349
+ structured_data = {
350
+ "types": types,
351
+ "domain": domain,
352
+ "primaryType": primary_type,
353
+ "message": message,
354
+ }
355
+ return encode_typed_data(full_message=structured_data)
356
+ ```
357
+
358
+ ##### b. EIP-712 Typed Data
359
+
360
+ The signing structure uses EIP-712 typed data, which consists of:
361
+
362
+ Domain: This is a structured domain object that helps to prevent cross-domain replay attacks. In this case, it typically includes the name field (which might be the name of the application or system).
363
+
364
+ Message: This is the main data being signed, which includes the accountIndex, wallet address, and tosAccepted fields.
365
+
366
+ Types: This describes the types of the fields in both the domain and message.
367
+
368
+ Primary Type: This indicates the primary type being signed (in this case, "AccountCreation").
369
+
370
+ ##### c. Encoding the Typed Data
371
+
372
+ The structure is encoded into a format that can be signed by the Ethereum account. This is done using the `encode_typed_data` function, which creates a `SignableMessage`. The `SignableMessage` includes the hash of the typed data according to the EIP-712 standard.
373
+
374
+ #### 3. Signing the Structure with the Ethereum Account
375
+
376
+ Once the signing structure is prepared, it is signed using the Ethereum private key.
377
+
378
+ #### 4. Deriving the Stark Private Key
379
+
380
+ The signature obtained from the Ethereum account is then used to derive the Stark private key. This is done by truncating the r value from the Ethereum signature and using it as the basis for the Stark private key:
381
+
382
+ ```python
383
+ def get_private_key_from_eth_signature(eth_signature: str) -> int:
384
+ eth_sig_truncated = re.sub("^0x", "", eth_signature)
385
+ r = eth_sig_truncated[:64]
386
+ return stark_sign.grind_key(int(r, 16), stark_sign.EC_ORDER)
387
+ ```
388
+
389
+ `stark_sign.grind_key` is a function imported from [`vendor/starkware/crypto/signature/signature.py`](vendor/starkware/crypto/signature/signature.py)
390
+
391
+ ## Depositing via SDK (Since Version 0.3.0)
392
+
393
+ There is a new function `deposit` available on the [`AccountModule`](x10/perpetual/trading_client/account_module.py) which provides the ability to directly deposit USDC into your StarkEx account. For more details check out `call_stark_perpetual_deposit` in [contract.py](x10/perpetual/contract.py)
394
+
395
+
396
+ ## Contribution
397
+
398
+ Make sure you have [poetry](https://python-poetry.org/) installed.
399
+
400
+ - Clone the repository: `git@github.com:x10xchange/python_sdk.git`
401
+ - Navigate to the project directory: `cd python_sdk`
402
+ - Create a virtual environment: `poetry shell`
403
+ - Install dependencies: `poetry install`
404
+ - Update `examples/placed_order_example.py` with your credentials
405
+ - Run it: `python -m examples.placed_order_example`
406
+
407
+ Custom commands:
408
+ - `make format` - format code with `black`
409
+ - `make lint` - run `safety`, `black`, `flake8` and `mypy` checks
410
+ - `make test` - run tests
411
+
412
+
413
+