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.
Files changed (75) hide show
  1. architect_py/__init__.py +432 -5
  2. architect_py/async_client.py +77 -43
  3. architect_py/client.py +11 -4
  4. architect_py/client.pyi +504 -0
  5. architect_py/common_types/__init__.py +2 -1
  6. architect_py/common_types/time_in_force.py +94 -0
  7. architect_py/common_types/tradable_product.py +9 -0
  8. architect_py/graphql_client/__init__.py +1 -216
  9. architect_py/graphql_client/client.py +2 -1043
  10. architect_py/graphql_client/enums.py +0 -72
  11. architect_py/graphql_client/fragments.py +4 -152
  12. architect_py/grpc/__init__.py +0 -143
  13. architect_py/grpc/client.py +6 -1
  14. architect_py/grpc/models/Core/RestartCptyRequest.py +40 -0
  15. architect_py/grpc/models/Core/RestartCptyResponse.py +20 -0
  16. architect_py/grpc/models/Marketdata/Liquidation.py +0 -1
  17. architect_py/grpc/models/Marketdata/Ticker.py +14 -1
  18. architect_py/grpc/models/Marketdata/TickersRequest.py +38 -12
  19. architect_py/grpc/models/Marketdata/Trade.py +0 -1
  20. architect_py/grpc/models/Oms/Order.py +33 -24
  21. architect_py/grpc/models/Oms/PlaceOrderRequest.py +33 -24
  22. architect_py/grpc/models/__init__.py +113 -2
  23. architect_py/grpc/models/definitions.py +2 -32
  24. architect_py/grpc/utils.py +1 -4
  25. architect_py/tests/test_order_entry.py +2 -3
  26. architect_py/tests/test_orderflow.py +1 -1
  27. architect_py/utils/pandas.py +4 -3
  28. architect_py-5.1.0.dist-info/METADATA +66 -0
  29. {architect_py-5.0.0b3.dist-info → architect_py-5.1.0.dist-info}/RECORD +48 -67
  30. {architect_py-5.0.0b3.dist-info → architect_py-5.1.0.dist-info}/WHEEL +1 -1
  31. examples/book_subscription.py +1 -2
  32. examples/candles.py +1 -3
  33. examples/common.py +1 -2
  34. examples/external_cpty.py +2 -2
  35. examples/funding_rate_mean_reversion_algo.py +9 -6
  36. examples/order_sending.py +29 -9
  37. examples/stream_l1_marketdata.py +1 -2
  38. examples/stream_l2_marketdata.py +1 -2
  39. examples/trades.py +1 -2
  40. examples/tutorial_async.py +6 -8
  41. examples/tutorial_sync.py +6 -7
  42. scripts/add_imports_to_inits.py +146 -0
  43. scripts/correct_sync_interface.py +143 -0
  44. scripts/postprocess_grpc.py +57 -11
  45. scripts/preprocess_grpc_schema.py +2 -0
  46. scripts/prune_graphql_schema.py +187 -0
  47. architect_py/client_interface.py +0 -63
  48. architect_py/common_types/scalars.py +0 -25
  49. architect_py/graphql_client/cancel_all_orders_mutation.py +0 -17
  50. architect_py/graphql_client/cancel_order_mutation.py +0 -23
  51. architect_py/graphql_client/create_jwt.py +0 -17
  52. architect_py/graphql_client/get_account_history_query.py +0 -27
  53. architect_py/graphql_client/get_account_query.py +0 -23
  54. architect_py/graphql_client/get_account_summaries_query.py +0 -27
  55. architect_py/graphql_client/get_account_summary_query.py +0 -25
  56. architect_py/graphql_client/get_candle_snapshot_query.py +0 -27
  57. architect_py/graphql_client/get_fills_query.py +0 -69
  58. architect_py/graphql_client/get_historical_orders_query.py +0 -27
  59. architect_py/graphql_client/get_l_1_book_snapshot_query.py +0 -21
  60. architect_py/graphql_client/get_l_1_book_snapshots_query.py +0 -23
  61. architect_py/graphql_client/get_l_2_book_snapshot_query.py +0 -25
  62. architect_py/graphql_client/get_market_status_query.py +0 -25
  63. architect_py/graphql_client/get_open_orders_query.py +0 -25
  64. architect_py/graphql_client/list_accounts_query.py +0 -23
  65. architect_py/graphql_client/place_order_mutation.py +0 -23
  66. architect_py/graphql_client/subscribe_candles.py +0 -16
  67. architect_py/graphql_client/subscribe_orderflow.py +0 -100
  68. architect_py/graphql_client/subscribe_trades.py +0 -27
  69. architect_py/graphql_client/user_email_query.py +0 -17
  70. architect_py/internal_utils/__init__.py +0 -0
  71. architect_py/internal_utils/no_pandas.py +0 -3
  72. architect_py-5.0.0b3.dist-info/METADATA +0 -123
  73. scripts/generate_sync_interface.py +0 -226
  74. {architect_py-5.0.0b3.dist-info → architect_py-5.1.0.dist-info}/licenses/LICENSE +0 -0
  75. {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(ClientProtocol):
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: Optional[str] = None,
45
- api_secret: Optional[str] = None,
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()
@@ -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
+ """
@@ -1,6 +1,7 @@
1
1
  from .order_dir import OrderDir
2
+ from .time_in_force import TimeInForce
2
3
  from .tradable_product import TradableProduct
3
4
 
4
5
  Venue = str
5
6
 
6
- __all__ = ["OrderDir", "TradableProduct", "Venue"]
7
+ __all__ = ["OrderDir", "TradableProduct", "Venue", "TimeInForce"]
@@ -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
  """