architect-py 5.0.0b3__py3-none-any.whl → 5.1.0__py3-none-any.whl
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.
- architect_py/__init__.py +432 -5
- architect_py/async_client.py +77 -43
- architect_py/client.py +11 -4
- architect_py/client.pyi +504 -0
- architect_py/common_types/__init__.py +2 -1
- architect_py/common_types/time_in_force.py +94 -0
- architect_py/common_types/tradable_product.py +9 -0
- architect_py/graphql_client/__init__.py +1 -216
- architect_py/graphql_client/client.py +2 -1043
- architect_py/graphql_client/enums.py +0 -72
- architect_py/graphql_client/fragments.py +4 -152
- architect_py/grpc/__init__.py +0 -143
- architect_py/grpc/client.py +6 -1
- architect_py/grpc/models/Core/RestartCptyRequest.py +40 -0
- architect_py/grpc/models/Core/RestartCptyResponse.py +20 -0
- architect_py/grpc/models/Marketdata/Liquidation.py +0 -1
- architect_py/grpc/models/Marketdata/Ticker.py +14 -1
- architect_py/grpc/models/Marketdata/TickersRequest.py +38 -12
- architect_py/grpc/models/Marketdata/Trade.py +0 -1
- architect_py/grpc/models/Oms/Order.py +33 -24
- architect_py/grpc/models/Oms/PlaceOrderRequest.py +33 -24
- architect_py/grpc/models/__init__.py +113 -2
- architect_py/grpc/models/definitions.py +2 -32
- architect_py/grpc/utils.py +1 -4
- architect_py/tests/test_order_entry.py +2 -3
- architect_py/tests/test_orderflow.py +1 -1
- architect_py/utils/pandas.py +4 -3
- architect_py-5.1.0.dist-info/METADATA +66 -0
- {architect_py-5.0.0b3.dist-info → architect_py-5.1.0.dist-info}/RECORD +48 -67
- {architect_py-5.0.0b3.dist-info → architect_py-5.1.0.dist-info}/WHEEL +1 -1
- examples/book_subscription.py +1 -2
- examples/candles.py +1 -3
- examples/common.py +1 -2
- examples/external_cpty.py +2 -2
- examples/funding_rate_mean_reversion_algo.py +9 -6
- examples/order_sending.py +29 -9
- examples/stream_l1_marketdata.py +1 -2
- examples/stream_l2_marketdata.py +1 -2
- examples/trades.py +1 -2
- examples/tutorial_async.py +6 -8
- examples/tutorial_sync.py +6 -7
- scripts/add_imports_to_inits.py +146 -0
- scripts/correct_sync_interface.py +143 -0
- scripts/postprocess_grpc.py +57 -11
- scripts/preprocess_grpc_schema.py +2 -0
- scripts/prune_graphql_schema.py +187 -0
- architect_py/client_interface.py +0 -63
- architect_py/common_types/scalars.py +0 -25
- architect_py/graphql_client/cancel_all_orders_mutation.py +0 -17
- architect_py/graphql_client/cancel_order_mutation.py +0 -23
- architect_py/graphql_client/create_jwt.py +0 -17
- architect_py/graphql_client/get_account_history_query.py +0 -27
- architect_py/graphql_client/get_account_query.py +0 -23
- architect_py/graphql_client/get_account_summaries_query.py +0 -27
- architect_py/graphql_client/get_account_summary_query.py +0 -25
- architect_py/graphql_client/get_candle_snapshot_query.py +0 -27
- architect_py/graphql_client/get_fills_query.py +0 -69
- architect_py/graphql_client/get_historical_orders_query.py +0 -27
- architect_py/graphql_client/get_l_1_book_snapshot_query.py +0 -21
- architect_py/graphql_client/get_l_1_book_snapshots_query.py +0 -23
- architect_py/graphql_client/get_l_2_book_snapshot_query.py +0 -25
- architect_py/graphql_client/get_market_status_query.py +0 -25
- architect_py/graphql_client/get_open_orders_query.py +0 -25
- architect_py/graphql_client/list_accounts_query.py +0 -23
- architect_py/graphql_client/place_order_mutation.py +0 -23
- architect_py/graphql_client/subscribe_candles.py +0 -16
- architect_py/graphql_client/subscribe_orderflow.py +0 -100
- architect_py/graphql_client/subscribe_trades.py +0 -27
- architect_py/graphql_client/user_email_query.py +0 -17
- architect_py/internal_utils/__init__.py +0 -0
- architect_py/internal_utils/no_pandas.py +0 -3
- architect_py-5.0.0b3.dist-info/METADATA +0 -123
- scripts/generate_sync_interface.py +0 -226
- {architect_py-5.0.0b3.dist-info → architect_py-5.1.0.dist-info}/licenses/LICENSE +0 -0
- {architect_py-5.0.0b3.dist-info → architect_py-5.1.0.dist-info}/top_level.txt +0 -0
architect_py/client.py
CHANGED
@@ -6,7 +6,6 @@ from functools import partial
|
|
6
6
|
from typing import Any, Awaitable, Callable, Coroutine, Optional, TypeVar
|
7
7
|
|
8
8
|
from .async_client import AsyncClient
|
9
|
-
from .client_interface import ClientProtocol
|
10
9
|
|
11
10
|
T = TypeVar("T")
|
12
11
|
|
@@ -17,7 +16,7 @@ def is_async_function(obj):
|
|
17
16
|
return callable(obj) and hasattr(obj, "__code__") and obj.__code__.co_flags & 0x80
|
18
17
|
|
19
18
|
|
20
|
-
class Client
|
19
|
+
class Client:
|
21
20
|
"""
|
22
21
|
This class is a wrapper around the AsyncClient class that allows you to call async methods synchronously.
|
23
22
|
This does not work for subscription based methods.
|
@@ -41,14 +40,22 @@ class Client(ClientProtocol):
|
|
41
40
|
def __init__(
|
42
41
|
self,
|
43
42
|
*,
|
44
|
-
api_key:
|
45
|
-
api_secret:
|
43
|
+
api_key: str,
|
44
|
+
api_secret: str,
|
46
45
|
paper_trading: bool,
|
47
46
|
endpoint: str = "https://app.architect.co",
|
48
47
|
graphql_port: Optional[int] = None,
|
49
48
|
event_loop: Optional[AbstractEventLoop] = None,
|
50
49
|
**kwargs,
|
51
50
|
):
|
51
|
+
"""
|
52
|
+
Create a new Client instance.
|
53
|
+
|
54
|
+
An `api_key` and `api_secret` can be created at https://app.architect.co/api-keys
|
55
|
+
|
56
|
+
Pass in an `event_loop` if you want to use your own; otherwise, this class
|
57
|
+
will use the default asyncio event loop.
|
58
|
+
"""
|
52
59
|
if event_loop is None:
|
53
60
|
try:
|
54
61
|
event_loop = asyncio.get_running_loop()
|
architect_py/client.pyi
ADDED
@@ -0,0 +1,504 @@
|
|
1
|
+
# fmt: off
|
2
|
+
# mypy: ignore-errors
|
3
|
+
# ruff: noqa
|
4
|
+
from architect_py.grpc.models import *
|
5
|
+
import asyncio
|
6
|
+
import pandas as pd
|
7
|
+
from architect_py.common_types import OrderDir as OrderDir, TimeInForce as TimeInForce, TradableProduct as TradableProduct, Venue as Venue
|
8
|
+
from architect_py.graphql_client import GraphQLClient as GraphQLClient
|
9
|
+
from architect_py.graphql_client.exceptions import GraphQLClientGraphQLMultiError as GraphQLClientGraphQLMultiError
|
10
|
+
from architect_py.graphql_client.fragments import ExecutionInfoFields as ExecutionInfoFields, ProductInfoFields as ProductInfoFields
|
11
|
+
from architect_py.grpc.client import GrpcClient as GrpcClient
|
12
|
+
from architect_py.grpc.models.Orderflow.OrderflowRequest import OrderflowRequestUnannotatedResponseType as OrderflowRequestUnannotatedResponseType, OrderflowRequest_route as OrderflowRequest_route
|
13
|
+
from architect_py.grpc.models.definitions import AccountIdOrName as AccountIdOrName, AccountWithPermissions as AccountWithPermissions, CandleWidth as CandleWidth, L2BookDiff as L2BookDiff, OrderId as OrderId, OrderSource as OrderSource, OrderType as OrderType, SortTickersBy as SortTickersBy, TraderIdOrEmail as TraderIdOrEmail
|
14
|
+
from architect_py.grpc.resolve_endpoint import resolve_endpoint as resolve_endpoint
|
15
|
+
from architect_py.utils.nearest_tick import TickRoundMethod as TickRoundMethod
|
16
|
+
from architect_py.utils.orderbook import update_orderbook_side as update_orderbook_side
|
17
|
+
from architect_py.utils.pandas import candles_to_dataframe as candles_to_dataframe
|
18
|
+
from architect_py.utils.price_bands import price_band_pairs as price_band_pairs
|
19
|
+
from architect_py.utils.symbol_parsing import nominative_expiration as nominative_expiration
|
20
|
+
from datetime import date, datetime
|
21
|
+
from decimal import Decimal
|
22
|
+
from typing import Any, AsyncGenerator, AsyncIterator, Literal, Sequence, overload
|
23
|
+
|
24
|
+
class Client:
|
25
|
+
"""
|
26
|
+
This class is a wrapper around the AsyncClient class that allows you to call async methods synchronously.
|
27
|
+
This does not work for subscription based methods.
|
28
|
+
|
29
|
+
This Client takes control of the event loop, which you can pass in.
|
30
|
+
|
31
|
+
One can find the function definition in the AsyncClient class.
|
32
|
+
|
33
|
+
The AsyncClient is more performant and powerful, so it is recommended to use that class if possible.
|
34
|
+
|
35
|
+
Avoid adding functions or other attributes to this class unless you know what you are doing, because
|
36
|
+
the __getattribute__ method changes the behavior of the class in a way that is not intuitive.
|
37
|
+
|
38
|
+
Instead, add them to the AsyncClient class.
|
39
|
+
"""
|
40
|
+
api_key: str | None
|
41
|
+
api_secret: str | None
|
42
|
+
paper_trading: bool
|
43
|
+
graphql_client: GraphQLClient
|
44
|
+
grpc_core: GrpcClient | None
|
45
|
+
grpc_marketdata: dict[Venue, GrpcClient]
|
46
|
+
grpc_hmart: GrpcClient | None
|
47
|
+
jwt: str | None
|
48
|
+
jwt_expiration: datetime | None
|
49
|
+
l1_books: dict[Venue, dict[TradableProduct, tuple[L1BookSnapshot, asyncio.Task]]]
|
50
|
+
def __init__(self, *, api_key: str, api_secret: str, paper_trading: bool, endpoint: str = 'https://app.architect.co', graphql_port: int | None = None, event_loop: asyncio.events.AbstractEventLoop | None = None) -> None:
|
51
|
+
"""
|
52
|
+
Create a new Client instance.
|
53
|
+
|
54
|
+
An `api_key` and `api_secret` can be created at https://app.architect.co/api-keys
|
55
|
+
|
56
|
+
Pass in an `event_loop` if you want to use your own; otherwise, this class
|
57
|
+
will use the default asyncio event loop.
|
58
|
+
"""
|
59
|
+
l2_books: dict[Venue, dict[TradableProduct, tuple[L2BookSnapshot, asyncio.Task]]]
|
60
|
+
def refresh_jwt(self, force: bool = False):
|
61
|
+
"""
|
62
|
+
Refresh the JWT for the gRPC channel if it's nearing expiration (within 1 minute).
|
63
|
+
If force=True, refresh the JWT unconditionally.
|
64
|
+
|
65
|
+
Query methods on Client that require auth will call this method internally.
|
66
|
+
"""
|
67
|
+
def set_jwt(self, jwt: str | None, jwt_expiration: datetime | None = None):
|
68
|
+
"""
|
69
|
+
Manually set the JWT for gRPC authentication.
|
70
|
+
|
71
|
+
Args:
|
72
|
+
jwt: the JWT to set;
|
73
|
+
None to clear the JWT
|
74
|
+
jwt_expiration: when to expire the JWT
|
75
|
+
"""
|
76
|
+
def discover_marketdata(self) -> None:
|
77
|
+
"""
|
78
|
+
Load marketdata endpoints from the server config.
|
79
|
+
|
80
|
+
The Architect core is responsible for telling you where to find marketdata as per
|
81
|
+
its configuration. You can also manually set marketdata endpoints by calling
|
82
|
+
set_marketdata directly.
|
83
|
+
|
84
|
+
This method is called on Client.connect.
|
85
|
+
"""
|
86
|
+
def set_marketdata(self, venue: Venue, endpoint: str):
|
87
|
+
"""
|
88
|
+
Manually set the marketdata endpoint for a venue.
|
89
|
+
"""
|
90
|
+
def marketdata(self, venue: Venue) -> GrpcClient:
|
91
|
+
"""
|
92
|
+
Get the marketdata client for a venue.
|
93
|
+
"""
|
94
|
+
def set_hmart(self, endpoint: str):
|
95
|
+
"""
|
96
|
+
Manually set the hmart (historical marketdata service) endpoint.
|
97
|
+
"""
|
98
|
+
def hmart(self) -> GrpcClient:
|
99
|
+
"""
|
100
|
+
Get the hmart (historical marketdata service) client.
|
101
|
+
"""
|
102
|
+
def core(self) -> GrpcClient:
|
103
|
+
"""
|
104
|
+
Get the core client.
|
105
|
+
"""
|
106
|
+
def who_am_i(self) -> tuple[str, str]:
|
107
|
+
"""
|
108
|
+
Gets the user_id and user_email for the user that the API key belongs to.
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
(user_id, user_email)
|
112
|
+
"""
|
113
|
+
def list_symbols(self, *, marketdata: Venue | None = None) -> list[str]:
|
114
|
+
"""
|
115
|
+
List all symbols.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
marketdata: query marketdata endpoint for the specified venue directly;
|
119
|
+
If provided, query the venue's marketdata endpoint directly,
|
120
|
+
instead of the Architect core. This is sometimes useful for
|
121
|
+
cross-referencing symbols or checking availability.
|
122
|
+
"""
|
123
|
+
def search_symbols(self, search_string: str | None = None, execution_venue: str | None = None, offset: int = 0, limit: int = 20) -> list[TradableProduct]:
|
124
|
+
'''
|
125
|
+
Search for tradable products on Architect.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
search_string: a string to search for in the symbol
|
129
|
+
Can be "*" for wild card search.
|
130
|
+
Examples: "ES", "NQ", "GC"
|
131
|
+
execution_venue: the execution venue to search in
|
132
|
+
Examples: "CME"
|
133
|
+
'''
|
134
|
+
def get_product_info(self, symbol: str) -> ProductInfoFields | None:
|
135
|
+
'''
|
136
|
+
Get information about a product, e.g. product_type, underlying, multiplier.
|
137
|
+
|
138
|
+
Args:
|
139
|
+
symbol: the symbol to get information for
|
140
|
+
the symbol should *not* have a quote,
|
141
|
+
ie "ES 20250620 CME Future" instead of "ES 20250620 CME Future/USD"
|
142
|
+
|
143
|
+
If you used TradableProduct, you can use the base() method to get the symbol
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
None if the symbol does not exist
|
147
|
+
'''
|
148
|
+
def get_product_infos(self, symbols: list[str] | None) -> Sequence[ProductInfoFields]:
|
149
|
+
"""
|
150
|
+
Get information about products, e.g. product_type, underlying, multiplier.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
symbols: the symbols to get information for, or None for all symbols
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
Product infos for each symbol. Not guaranteed to contain all symbols
|
157
|
+
that were asked for, or in the same order; any duplicates or invalid
|
158
|
+
symbols will be ignored.
|
159
|
+
"""
|
160
|
+
def get_execution_info(self, symbol: TradableProduct | str, execution_venue: str) -> ExecutionInfoFields | None:
|
161
|
+
'''
|
162
|
+
Get information about tradable product execution, e.g. tick_size,
|
163
|
+
step_size, margins.
|
164
|
+
|
165
|
+
Args:
|
166
|
+
symbol: the symbol to get execution information for
|
167
|
+
execution_venue: the execution venue e.g. "CME"
|
168
|
+
|
169
|
+
Returns:
|
170
|
+
None if the symbol doesn\'t exist
|
171
|
+
'''
|
172
|
+
def get_execution_infos(self, symbols: list[TradableProduct | str] | None, execution_venue: str | None = None) -> Sequence[ExecutionInfoFields]:
|
173
|
+
'''
|
174
|
+
Get information about tradable product execution, e.g. tick_size,
|
175
|
+
step_size, margins, for many symbols.
|
176
|
+
|
177
|
+
Args:
|
178
|
+
symbols: the symbols to get execution information for, or None for all symbols
|
179
|
+
execution_venue: the execution venue e.g. "CME"
|
180
|
+
|
181
|
+
Returns:
|
182
|
+
Execution infos for each symbol. Not guaranteed to contain all symbols
|
183
|
+
that were asked for, or in the same order; any duplicates or invalid
|
184
|
+
symbols will be ignored.
|
185
|
+
'''
|
186
|
+
def get_cme_first_notice_date(self, symbol: str) -> date | None:
|
187
|
+
'''
|
188
|
+
@deprecated(reason="Use get_product_info instead; first_notice_date is now a field")
|
189
|
+
|
190
|
+
Get the first notice date for a CME future.
|
191
|
+
|
192
|
+
Args:
|
193
|
+
symbol: the symbol to get the first notice date for a CME future
|
194
|
+
|
195
|
+
Returns:
|
196
|
+
The first notice date as a date object if it exists
|
197
|
+
'''
|
198
|
+
def get_future_series(self, series_symbol: str) -> list[str]:
|
199
|
+
'''
|
200
|
+
@deprecated(reason="Use get_futures_series instead")
|
201
|
+
'''
|
202
|
+
def get_futures_series(self, series_symbol: str) -> list[str]:
|
203
|
+
'''
|
204
|
+
List all futures in a given series.
|
205
|
+
|
206
|
+
Args:
|
207
|
+
series_symbol: the futures series
|
208
|
+
e.g. "ES CME Futures" would yield a list of all the ES futures
|
209
|
+
Returns:
|
210
|
+
List of futures products
|
211
|
+
'''
|
212
|
+
def get_front_future(self, series_symbol: str, venue: str, by_volume: bool = True) -> str:
|
213
|
+
'''
|
214
|
+
Gets the future with the most volume in a series.
|
215
|
+
|
216
|
+
Args:
|
217
|
+
series_symbol: the futures series
|
218
|
+
e.g. "ES CME Futures" would yield the lead future for the ES series
|
219
|
+
venue: the venue to get the lead future for, e.g. "CME"
|
220
|
+
by_volume: if True, sort by volume; otherwise sort by expiration date
|
221
|
+
|
222
|
+
Returns:
|
223
|
+
The lead future symbol
|
224
|
+
'''
|
225
|
+
@staticmethod
|
226
|
+
def get_expiration_from_CME_name(name: str) -> date | None:
|
227
|
+
'''
|
228
|
+
@deprecated(reason="Use utils.symbol_parsing.nominative_expiration instead")
|
229
|
+
|
230
|
+
Get the expiration date from a CME future name.
|
231
|
+
|
232
|
+
Args:
|
233
|
+
name: the CME future name
|
234
|
+
e.g. "ES 20211217 CME Future" -> date(2021, 12, 17)
|
235
|
+
Returns:
|
236
|
+
the expiration date as a date object
|
237
|
+
'''
|
238
|
+
def get_cme_futures_series(self, series: str) -> list[tuple[date, str]]:
|
239
|
+
'''
|
240
|
+
@deprecated(reason="Use get_futures_series instead")
|
241
|
+
|
242
|
+
List all futures in a given CME series.
|
243
|
+
|
244
|
+
Args:
|
245
|
+
series: the series to get the futures for
|
246
|
+
e.g. "ES CME Futures"
|
247
|
+
Returns:
|
248
|
+
a list of tuples of the expiration date and
|
249
|
+
the symbol for each future in the series
|
250
|
+
|
251
|
+
e.g.
|
252
|
+
```
|
253
|
+
[
|
254
|
+
(datetime.date(2025, 3, 21), \'ES 20250321 CME Future\'),
|
255
|
+
(datetime.date(2025, 6, 20), \'ES 20250620 CME Future\'),
|
256
|
+
(datetime.date(2025, 9, 19), \'ES 20250919 CME Future\'),
|
257
|
+
# ...
|
258
|
+
]
|
259
|
+
```
|
260
|
+
'''
|
261
|
+
def get_cme_future_from_root_month_year(self, root: str, month: int, year: int) -> str:
|
262
|
+
'''
|
263
|
+
Get the symbol for a CME future from the root, month, and year.
|
264
|
+
This is a simple wrapper around search_symbols.
|
265
|
+
|
266
|
+
Args:
|
267
|
+
root: the root symbol for the future e.g. "ES"
|
268
|
+
month: the month of the future
|
269
|
+
year: the year of the future
|
270
|
+
Returns:
|
271
|
+
The future symbol if it exists and is unique.
|
272
|
+
'''
|
273
|
+
def get_market_status(self, symbol: TradableProduct | str, venue: Venue) -> MarketStatus:
|
274
|
+
'''
|
275
|
+
Returns market status for symbol (e.g. if it\'s currently quoting or trading).
|
276
|
+
|
277
|
+
Args:
|
278
|
+
symbol: the symbol to get the market status for, e.g. "ES 20250321 CME Future/USD"
|
279
|
+
venue: the venue that the symbol is traded at, e.g. CME
|
280
|
+
'''
|
281
|
+
def get_market_snapshot(self, symbol: TradableProduct | str, venue: Venue) -> L1BookSnapshot:
|
282
|
+
'''
|
283
|
+
@deprecated(reason="Use get_l1_snapshot instead")
|
284
|
+
|
285
|
+
This is an alias for l1_book_snapshot.
|
286
|
+
|
287
|
+
Args:
|
288
|
+
symbol: the symbol to get the market snapshot for, e.g. "ES 20250321 CME Future/USD"
|
289
|
+
venue: the venue that the symbol is traded at, e.g. CME
|
290
|
+
Returns:
|
291
|
+
L1BookSnapshot for the symbol
|
292
|
+
'''
|
293
|
+
def get_market_snapshots(self, symbols: list[TradableProduct | str], venue: Venue) -> Sequence[L1BookSnapshot]:
|
294
|
+
'''
|
295
|
+
@deprecated(reason="Use get_l1_snapshots instead")
|
296
|
+
|
297
|
+
This is an alias for l1_book_snapshots.
|
298
|
+
|
299
|
+
Args:
|
300
|
+
symbols: the symbols to get the market snapshots for
|
301
|
+
venue: the venue that the symbols are traded at
|
302
|
+
'''
|
303
|
+
@overload
|
304
|
+
def get_historical_candles(self, symbol: TradableProduct | str, venue: Venue, candle_width: CandleWidth, start: datetime, end: datetime, *, as_dataframe: Literal[True]) -> pd.DataFrame: ...
|
305
|
+
@overload
|
306
|
+
def get_historical_candles(self, symbol: TradableProduct | str, venue: Venue, candle_width: CandleWidth, start: datetime, end: datetime) -> list[Candle]: ...
|
307
|
+
def get_l1_book_snapshot(self, symbol: TradableProduct | str, venue: Venue) -> L1BookSnapshot:
|
308
|
+
"""
|
309
|
+
Gets the L1 book snapshot for a symbol.
|
310
|
+
|
311
|
+
Args:
|
312
|
+
symbol: the symbol to get the l1 book snapshot for
|
313
|
+
venue: the venue that the symbol is traded at
|
314
|
+
"""
|
315
|
+
def get_l1_book_snapshots(self, symbols: list[TradableProduct | str], venue: Venue) -> Sequence[L1BookSnapshot]:
|
316
|
+
"""
|
317
|
+
Gets the L1 book snapshots for a list of symbols.
|
318
|
+
|
319
|
+
Args:
|
320
|
+
symbols: the symbols to get the l1 book snapshots for
|
321
|
+
venue: the venue that the symbols are traded at
|
322
|
+
"""
|
323
|
+
def get_l2_book_snapshot(self, symbol: TradableProduct | str, venue: Venue) -> L2BookSnapshot:
|
324
|
+
"""
|
325
|
+
Gets the L2 book snapshot for a symbol.
|
326
|
+
|
327
|
+
Args:
|
328
|
+
symbol: the symbol to get the l2 book snapshot for
|
329
|
+
venue: the venue that the symbol is traded at
|
330
|
+
"""
|
331
|
+
def get_ticker(self, symbol: TradableProduct | str, venue: Venue) -> Ticker:
|
332
|
+
"""
|
333
|
+
Gets the ticker for a symbol.
|
334
|
+
"""
|
335
|
+
def list_accounts(self) -> list[AccountWithPermissions]:
|
336
|
+
"""
|
337
|
+
List accounts for the user that the API key belongs to.
|
338
|
+
|
339
|
+
Returns:
|
340
|
+
a list of AccountWithPermissionsFields for the user that the API key belongs to
|
341
|
+
a list of AccountWithPermissions for the user that the API key belongs to
|
342
|
+
(use who_am_i to get the user_id / email)
|
343
|
+
"""
|
344
|
+
def get_account_summary(self, account: str) -> AccountSummary:
|
345
|
+
'''
|
346
|
+
Get account summary, including balances, positions, pnls, etc.
|
347
|
+
|
348
|
+
Args:
|
349
|
+
account: account uuid or name
|
350
|
+
Examples: "00000000-0000-0000-0000-000000000000", "STONEX:000000/JDoe"
|
351
|
+
'''
|
352
|
+
def get_account_summaries(self, accounts: list[str] | None = None, trader: str | None = None) -> list[AccountSummary]:
|
353
|
+
"""
|
354
|
+
Get account summaries for accounts matching the filters.
|
355
|
+
|
356
|
+
Args:
|
357
|
+
accounts: list of account uuids or names
|
358
|
+
trader: if specified, return summaries for all accounts for this trader
|
359
|
+
|
360
|
+
If both arguments are given, the union of matching accounts are returned.
|
361
|
+
"""
|
362
|
+
def get_account_history(self, account: str, from_inclusive: datetime | None = None, to_exclusive: datetime | None = None) -> list[AccountSummary]:
|
363
|
+
"""
|
364
|
+
Get historical sequence of account summaries for the given account.
|
365
|
+
"""
|
366
|
+
def get_open_orders(self, order_ids: list[OrderId] | None = None, venue: str | None = None, account: str | None = None, trader: str | None = None, symbol: str | None = None, parent_order_id: OrderId | None = None) -> list[Order]:
|
367
|
+
"""
|
368
|
+
Returns a list of open orders for the user that match the filters.
|
369
|
+
|
370
|
+
Args:
|
371
|
+
order_ids: a list of order ids to get
|
372
|
+
venue: the venue to get orders for
|
373
|
+
account: the account to get orders for
|
374
|
+
trader: the trader to get orders for
|
375
|
+
symbol: the symbol to get orders for
|
376
|
+
parent_order_id: the parent order id to get orders for
|
377
|
+
|
378
|
+
Returns:
|
379
|
+
Open orders that match the union of the filters
|
380
|
+
"""
|
381
|
+
def get_all_open_orders(self) -> list[Order]:
|
382
|
+
'''
|
383
|
+
@deprecated(reason="Use get_open_orders with no parameters instead")
|
384
|
+
|
385
|
+
Returns a list of all open orders for the authenticated user.
|
386
|
+
'''
|
387
|
+
def get_historical_orders(self, order_ids: list[OrderId] | None = None, from_inclusive: datetime | None = None, to_exclusive: datetime | None = None, venue: str | None = None, account: str | None = None, parent_order_id: OrderId | None = None) -> list[Order]:
|
388
|
+
"""
|
389
|
+
Returns a list of all historical orders that match the filters.
|
390
|
+
|
391
|
+
Historical orders are orders that are not open, having been filled,
|
392
|
+
canceled, expired, or outed.
|
393
|
+
|
394
|
+
Args:
|
395
|
+
order_ids: a list of order ids to get
|
396
|
+
from_inclusive: the start date to get orders for
|
397
|
+
to_exclusive: the end date to get orders for
|
398
|
+
venue: the venue to get orders for, e.g. CME
|
399
|
+
account: account uuid or name
|
400
|
+
parent_order_id: the parent order id to get orders for
|
401
|
+
Returns:
|
402
|
+
Historical orders that match the union of the filters.
|
403
|
+
|
404
|
+
If order_ids is not specified, then from_inclusive and to_exclusive
|
405
|
+
MUST be specified.
|
406
|
+
"""
|
407
|
+
def get_order(self, order_id: OrderId) -> Order | None:
|
408
|
+
"""
|
409
|
+
Returns the specified order. Useful for looking at past sent orders.
|
410
|
+
Queries open_orders first, then queries historical_orders.
|
411
|
+
|
412
|
+
Args:
|
413
|
+
order_id: the order id to get
|
414
|
+
"""
|
415
|
+
def get_orders(self, order_ids: list[OrderId]) -> list[Order | None]:
|
416
|
+
"""
|
417
|
+
Returns the specified orders. Useful for looking at past sent orders.
|
418
|
+
Plural form of get_order.
|
419
|
+
|
420
|
+
Args:
|
421
|
+
order_ids: a list of order ids to get
|
422
|
+
"""
|
423
|
+
def get_fills(self, from_inclusive: datetime | None = None, to_exclusive: datetime | None = None, venue: str | None = None, account: str | None = None, order_id: OrderId | None = None, limit: int | None = None) -> HistoricalFillsResponse:
|
424
|
+
'''
|
425
|
+
Returns all fills matching the given filters.
|
426
|
+
|
427
|
+
Args:
|
428
|
+
from_inclusive: the start date to get fills for
|
429
|
+
to_exclusive: the end date to get fills for
|
430
|
+
venue: the venue to get fills for, e.g. "CME"
|
431
|
+
account: account uuid or name
|
432
|
+
order_id: the order id to get fills for
|
433
|
+
'''
|
434
|
+
def send_limit_order(self, *args, **kwargs) -> Order:
|
435
|
+
'''
|
436
|
+
@deprecated(reason="Use place_limit_order instead")
|
437
|
+
'''
|
438
|
+
def place_limit_order(self, *, id: OrderId | None = None, symbol: TradableProduct | str, execution_venue: str | None = None, dir: OrderDir | None = None, quantity: Decimal, limit_price: Decimal, order_type: OrderType = ..., time_in_force: TimeInForce = ..., price_round_method: TickRoundMethod | None = None, account: str | None = None, trader: str | None = None, post_only: bool = False, trigger_price: Decimal | None = None, **kwargs: Any) -> Order:
|
439
|
+
'''
|
440
|
+
Sends a regular limit order.
|
441
|
+
|
442
|
+
Args:
|
443
|
+
id: in case user wants to generate their own order id, otherwise it will be generated automatically
|
444
|
+
symbol: the symbol to send the order for
|
445
|
+
execution_venue: the execution venue to send the order to,
|
446
|
+
if execution_venue is set to None, the OMS will send the order to the primary_exchange
|
447
|
+
the primary_exchange can be deduced from `get_product_info`
|
448
|
+
dir: the direction of the order, BUY or SELL
|
449
|
+
quantity: the quantity of the order
|
450
|
+
limit_price: the limit price of the order
|
451
|
+
It is highly recommended to make this a Decimal object from the decimal module to avoid floating point errors
|
452
|
+
order_type: the type of the order
|
453
|
+
time_in_force: the time in force of the order
|
454
|
+
price_round_method: the method to round the price to the nearest tick, will not round if None
|
455
|
+
account: the account to send the order for
|
456
|
+
While technically optional, for most order types, the account is required
|
457
|
+
trader: the trader to send the order for, defaults to the user\'s trader
|
458
|
+
for when sending order for another user, not relevant for vast majority of users
|
459
|
+
post_only: whether the order should be post only, not supported by all exchanges
|
460
|
+
trigger_price: the trigger price for the order, only relevant for stop / take_profit orders
|
461
|
+
Returns:
|
462
|
+
the Order object for the order
|
463
|
+
The order.status should be "PENDING" until the order is "OPEN" / "REJECTED" / "OUT" / "CANCELED" / "STALE"
|
464
|
+
|
465
|
+
If the order is rejected, the order.reject_reason and order.reject_message will be set
|
466
|
+
'''
|
467
|
+
def send_market_pro_order(self, *, id: OrderId | None = None, symbol: TradableProduct | str, execution_venue: str, dir: OrderDir, quantity: Decimal, time_in_force: TimeInForce = ..., account: str | None = None, fraction_through_market: Decimal = ...) -> Order:
|
468
|
+
'''
|
469
|
+
Sends a market-order like limit price based on the BBO.
|
470
|
+
Meant to behave as a market order but with more protections.
|
471
|
+
|
472
|
+
Args:
|
473
|
+
id: in case user wants to generate their own order id, otherwise it will be generated automatically
|
474
|
+
symbol: the symbol to send the order for
|
475
|
+
execution_venue: the execution venue to send the order to
|
476
|
+
dir: the direction of the order
|
477
|
+
quantity: the quantity of the order
|
478
|
+
time_in_force: the time in force of the order
|
479
|
+
account: the account to send the order for
|
480
|
+
fraction_through_market: the fraction through the market to send the order at
|
481
|
+
e.g. 0.001 would send the order 0.1% through the market
|
482
|
+
Returns:
|
483
|
+
the OrderFields object for the order
|
484
|
+
The order.status should be "PENDING" until the order is "OPEN" / "REJECTED" / "OUT" / "CANCELED" / "STALE"
|
485
|
+
|
486
|
+
If the order is rejected, the order.reject_reason and order.reject_message will be set
|
487
|
+
'''
|
488
|
+
def cancel_order(self, order_id: OrderId) -> Cancel:
|
489
|
+
"""
|
490
|
+
Cancels an order by order id.
|
491
|
+
|
492
|
+
Args:
|
493
|
+
order_id: the order id to cancel
|
494
|
+
Returns:
|
495
|
+
the CancelFields object
|
496
|
+
"""
|
497
|
+
def cancel_all_orders(self, account: AccountIdOrName | None = None, execution_venue: str | None = None, trader: TraderIdOrEmail | None = None) -> bool:
|
498
|
+
"""
|
499
|
+
Cancels all open orders.
|
500
|
+
|
501
|
+
Returns:
|
502
|
+
True if all orders were cancelled successfully
|
503
|
+
False if there was an error
|
504
|
+
"""
|
@@ -0,0 +1,94 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from typing import ClassVar, Literal, Optional, overload
|
3
|
+
|
4
|
+
import msgspec
|
5
|
+
|
6
|
+
|
7
|
+
class TimeInForce:
|
8
|
+
"""
|
9
|
+
A type-checker-friendly way to get enum-like constants + a payload-carrying variant (GTD) in one class.
|
10
|
+
Usage:
|
11
|
+
Simply use the class as an enum for the standard time in force values:
|
12
|
+
TimeInForce.GTC # Good Till Cancelled
|
13
|
+
TimeInForce.DAY # Day Order
|
14
|
+
TimeInForce.IOC # Immediate or Cancel
|
15
|
+
TimeInForce.FOK # Fill or Kill
|
16
|
+
TimeInForce.ATO # At the Opening
|
17
|
+
TimeInForce.ATC # At the Close
|
18
|
+
|
19
|
+
To specify a GTD (Good Till Date) order, use the GTD method:
|
20
|
+
TimeInForce.GTD(datetime(2023, 1, 1, 0, 0, 0, tzinfo=timezone.utc))
|
21
|
+
TimeInForce.GTD(datetime.now(timezone.utc))
|
22
|
+
"""
|
23
|
+
|
24
|
+
__slots__ = ("kind", "date")
|
25
|
+
kind: Literal["GTC", "DAY", "IOC", "FOK", "ATO", "ATC", "GTD"]
|
26
|
+
date: Optional[datetime]
|
27
|
+
|
28
|
+
GTC: ClassVar["TimeInForce"]
|
29
|
+
DAY: ClassVar["TimeInForce"]
|
30
|
+
IOC: ClassVar["TimeInForce"]
|
31
|
+
FOK: ClassVar["TimeInForce"]
|
32
|
+
ATO: ClassVar["TimeInForce"]
|
33
|
+
ATC: ClassVar["TimeInForce"]
|
34
|
+
|
35
|
+
_CACHE: ClassVar[dict[str, "TimeInForce"]] = {}
|
36
|
+
|
37
|
+
@overload
|
38
|
+
def __init__(self, kind: Literal["GTD"], date: datetime) -> None: ...
|
39
|
+
|
40
|
+
@overload
|
41
|
+
def __init__(
|
42
|
+
self,
|
43
|
+
kind: Literal["GTC", "DAY", "IOC", "FOK", "ATO", "ATC"],
|
44
|
+
) -> None: ...
|
45
|
+
|
46
|
+
def __init__(
|
47
|
+
self,
|
48
|
+
kind: Literal["GTC", "DAY", "IOC", "FOK", "ATO", "ATC", "GTD"],
|
49
|
+
date: Optional[datetime] = None,
|
50
|
+
) -> None:
|
51
|
+
self.kind = kind
|
52
|
+
self.date = date
|
53
|
+
|
54
|
+
def __new__(cls, kind: str, date: Optional[datetime] = None):
|
55
|
+
# all singletons will be pointer-identical, useful for comparison and hashing
|
56
|
+
if date is None and kind != "GTD":
|
57
|
+
# return / create singleton
|
58
|
+
if kind in cls._CACHE:
|
59
|
+
return cls._CACHE[kind]
|
60
|
+
self = super().__new__(cls)
|
61
|
+
cls._CACHE[kind] = self
|
62
|
+
return self
|
63
|
+
# GTD or non-cached path
|
64
|
+
return super().__new__(cls)
|
65
|
+
|
66
|
+
@classmethod
|
67
|
+
def GTD(cls, when: datetime) -> "TimeInForce":
|
68
|
+
return cls("GTD", when)
|
69
|
+
|
70
|
+
def serialize(self) -> msgspec.Raw:
|
71
|
+
if self.kind == "GTD":
|
72
|
+
assert self.date is not None
|
73
|
+
return msgspec.Raw(f'{{"GTD": "{self.date.isoformat()}"}}'.encode())
|
74
|
+
return msgspec.Raw(f'"{self.kind}"'.encode())
|
75
|
+
|
76
|
+
@staticmethod
|
77
|
+
def deserialize(s) -> "TimeInForce":
|
78
|
+
if isinstance(s, dict):
|
79
|
+
return TimeInForce.GTD(datetime.fromisoformat(s["GTD"]))
|
80
|
+
return TimeInForce(s)
|
81
|
+
|
82
|
+
def __repr__(self) -> str:
|
83
|
+
if self.kind == "GTD":
|
84
|
+
assert self.date is not None
|
85
|
+
return f"<TimeInForce.GTD({self.date.isoformat()})>"
|
86
|
+
return f"<TimeInForce.{self.kind}>"
|
87
|
+
|
88
|
+
|
89
|
+
TimeInForce.GTC = TimeInForce("GTC")
|
90
|
+
TimeInForce.DAY = TimeInForce("DAY")
|
91
|
+
TimeInForce.IOC = TimeInForce("IOC")
|
92
|
+
TimeInForce.FOK = TimeInForce("FOK")
|
93
|
+
TimeInForce.ATO = TimeInForce("ATO")
|
94
|
+
TimeInForce.ATC = TimeInForce("ATC")
|
@@ -1,5 +1,7 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
|
3
|
+
import msgspec
|
4
|
+
|
3
5
|
|
4
6
|
class TradableProduct(str):
|
5
7
|
"""
|
@@ -51,6 +53,13 @@ class TradableProduct(str):
|
|
51
53
|
def quote(self) -> str:
|
52
54
|
return self.split("/", 1)[1]
|
53
55
|
|
56
|
+
def serialize(self) -> msgspec.Raw:
|
57
|
+
return msgspec.Raw(self.encode())
|
58
|
+
|
59
|
+
@staticmethod
|
60
|
+
def deserialize(s: str) -> "TradableProduct":
|
61
|
+
return TradableProduct(s)
|
62
|
+
|
54
63
|
|
55
64
|
def parse_tradable_product(value: str) -> TradableProduct:
|
56
65
|
"""
|