architect-py 5.1.3__py3-none-any.whl → 5.1.4b1__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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # ruff: noqa:I001
2
2
 
3
- __version__ = "5.1.3"
3
+ __version__ = "5.1.4b1"
4
4
 
5
5
  from .utils.nearest_tick import TickRoundMethod
6
6
  from .async_client import AsyncClient
@@ -11,6 +11,7 @@ from typing import (
11
11
  Literal,
12
12
  Optional,
13
13
  Sequence,
14
+ Tuple,
14
15
  Union,
15
16
  overload,
16
17
  )
@@ -56,6 +57,7 @@ class AsyncClient:
56
57
  api_secret: Optional[str] = None
57
58
  paper_trading: bool
58
59
  graphql_client: GraphQLClient
60
+ grpc_options: Sequence[Tuple[str, Any]] | None = None
59
61
  grpc_core: Optional[GrpcClient] = None
60
62
  grpc_marketdata: dict[Venue, GrpcClient] = {}
61
63
  grpc_hmart: Optional[GrpcClient] = None
@@ -77,14 +79,22 @@ class AsyncClient:
77
79
  paper_trading: bool,
78
80
  endpoint: str = "https://app.architect.co",
79
81
  graphql_port: Optional[int] = None,
82
+ grpc_options: Sequence[Tuple[str, Any]] | None = None,
80
83
  **kwargs: Any,
81
84
  ) -> "AsyncClient":
82
85
  """
83
86
  Connect to an Architect installation.
84
87
 
85
- An `api_key` and `api_secret` can be created at https://app.architect.co/api-keys
88
+ An `api_key` and `api_secret` can be created at https://app.architect.co/api-keys.
86
89
 
87
90
  Raises ValueError if the API key and secret are not the correct length or contain invalid characters.
91
+
92
+ ## Advanced configuration
93
+
94
+ ### gRPC channel options
95
+
96
+ Use `grpc_options` to configure gRPC channels created by this client.
97
+ See https://grpc.github.io/grpc/python/glossary.html#term-channel_arguments for reference.
88
98
  """
89
99
  if paper_trading:
90
100
  COLOR = "\033[30;43m"
@@ -123,6 +133,7 @@ class AsyncClient:
123
133
  paper_trading=paper_trading,
124
134
  grpc_host=grpc_host,
125
135
  grpc_port=grpc_port,
136
+ grpc_options=grpc_options,
126
137
  graphql_port=graphql_port,
127
138
  use_ssl=use_ssl,
128
139
  _i_know_what_i_am_doing=True,
@@ -144,6 +155,7 @@ class AsyncClient:
144
155
  paper_trading: bool,
145
156
  grpc_host: str = "app.architect.co",
146
157
  grpc_port: int,
158
+ grpc_options: Sequence[Tuple[str, Any]] | None = None,
147
159
  graphql_port: Optional[int] = None,
148
160
  use_ssl: bool = True,
149
161
  _i_know_what_i_am_doing: bool = False,
@@ -179,7 +191,10 @@ class AsyncClient:
179
191
  api_key=api_key,
180
192
  api_secret=api_secret,
181
193
  )
182
- self.grpc_core = GrpcClient(host=grpc_host, port=grpc_port, use_ssl=use_ssl)
194
+ self.grpc_options = grpc_options
195
+ self.grpc_core = GrpcClient(
196
+ host=grpc_host, port=grpc_port, use_ssl=use_ssl, options=grpc_options
197
+ )
183
198
 
184
199
  async def close(self):
185
200
  """
@@ -268,7 +283,10 @@ class AsyncClient:
268
283
  use_ssl,
269
284
  )
270
285
  self.grpc_marketdata[venue] = GrpcClient(
271
- host=grpc_host, port=grpc_port, use_ssl=use_ssl
286
+ host=grpc_host,
287
+ port=grpc_port,
288
+ use_ssl=use_ssl,
289
+ options=self.grpc_options,
272
290
  )
273
291
  except Exception as e:
274
292
  logging.error("Failed to set marketdata endpoint: %s", e)
@@ -282,7 +300,10 @@ class AsyncClient:
282
300
  try:
283
301
  grpc_host, grpc_port, use_ssl = await resolve_endpoint(endpoint)
284
302
  self.grpc_marketdata[venue] = GrpcClient(
285
- host=grpc_host, port=grpc_port, use_ssl=use_ssl
303
+ host=grpc_host,
304
+ port=grpc_port,
305
+ use_ssl=use_ssl,
306
+ options=self.grpc_options,
286
307
  )
287
308
  logging.debug(
288
309
  f"Setting marketdata endpoint for {venue}: {grpc_host}:{grpc_port} use_ssl={use_ssl}"
@@ -315,7 +336,10 @@ class AsyncClient:
315
336
  use_ssl,
316
337
  )
317
338
  self.grpc_hmart = GrpcClient(
318
- host=grpc_host, port=grpc_port, use_ssl=use_ssl
339
+ host=grpc_host,
340
+ port=grpc_port,
341
+ use_ssl=use_ssl,
342
+ options=self.grpc_options,
319
343
  )
320
344
  except Exception as e:
321
345
  logging.error("Failed to set hmart endpoint: %s", e)
@@ -364,7 +388,9 @@ class AsyncClient:
364
388
  "as of v5.0.0: enable_orderflow is deprecated; orderflow is enabled by default"
365
389
  )
366
390
 
367
- async def cpty_status(self, kind: str, instance: Optional[str] = None) -> CptyStatus:
391
+ async def cpty_status(
392
+ self, kind: str, instance: Optional[str] = None
393
+ ) -> CptyStatus:
368
394
  """
369
395
  Get cpty status.
370
396
  """
architect_py/client.py CHANGED
@@ -4,7 +4,15 @@ import threading
4
4
  from asyncio import AbstractEventLoop
5
5
  from collections.abc import Callable
6
6
  from functools import partial
7
- from typing import Any, Concatenate, Coroutine, Optional, ParamSpec, TypeVar
7
+ from typing import (
8
+ Any,
9
+ Concatenate,
10
+ Coroutine,
11
+ Optional,
12
+ ParamSpec,
13
+ Sequence,
14
+ TypeVar,
15
+ )
8
16
 
9
17
  from .async_client import AsyncClient
10
18
 
@@ -50,6 +58,7 @@ class Client:
50
58
  paper_trading: bool,
51
59
  endpoint: str = "https://app.architect.co",
52
60
  graphql_port: Optional[int] = None,
61
+ grpc_options: Sequence[tuple[str, Any]] | None = None,
53
62
  event_loop: Optional[AbstractEventLoop] = None,
54
63
  **kwargs,
55
64
  ):
@@ -78,6 +87,7 @@ class Client:
78
87
  paper_trading=paper_trading,
79
88
  endpoint=endpoint,
80
89
  graphql_port=graphql_port,
90
+ grpc_options=grpc_options,
81
91
  **kwargs,
82
92
  )
83
93
 
architect_py/client.pyi CHANGED
@@ -38,13 +38,14 @@ class Client:
38
38
  api_secret: str | None
39
39
  paper_trading: bool
40
40
  graphql_client: GraphQLClient
41
+ grpc_options: Sequence[tuple[str, Any]] | None
41
42
  grpc_core: GrpcClient | None
42
43
  grpc_marketdata: dict[Venue, GrpcClient]
43
44
  grpc_hmart: GrpcClient | None
44
45
  jwt: str | None
45
46
  jwt_expiration: datetime | None
46
47
  l1_books: dict[Venue, dict[TradableProduct, tuple[L1BookSnapshot, asyncio.Task]]]
47
- 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:
48
+ def __init__(self, *, api_key: str, api_secret: str, paper_trading: bool, endpoint: str = 'https://app.architect.co', graphql_port: int | None = None, grpc_options: Sequence[tuple[str, Any]] | None = None, event_loop: asyncio.events.AbstractEventLoop | None = None) -> None:
48
49
  """
49
50
  Create a new Client instance.
50
51
 
@@ -118,6 +119,10 @@ class Client:
118
119
  Returns:
119
120
  (user_id, user_email)
120
121
  """
122
+ def cpty_status(self, kind: str, instance: str | None = None) -> CptyStatus:
123
+ """
124
+ Get cpty status.
125
+ """
121
126
  def list_symbols(self, *, marketdata: Venue | None = None) -> list[str]:
122
127
  """
123
128
  List all symbols.
@@ -1,5 +1,5 @@
1
1
  from types import UnionType
2
- from typing import AsyncIterator
2
+ from typing import Any, AsyncIterator, Sequence, Tuple
3
3
 
4
4
  import grpc
5
5
  import msgspec
@@ -18,14 +18,23 @@ class GrpcClient:
18
18
  channel: grpc.aio.Channel
19
19
  jwt: str | None = None
20
20
 
21
- def __init__(self, *, host: str, port: int, use_ssl: bool = False):
21
+ def __init__(
22
+ self,
23
+ *,
24
+ host: str,
25
+ port: int,
26
+ use_ssl: bool = False,
27
+ options: Sequence[Tuple[str, Any]] | None = None,
28
+ ):
22
29
  scheme = "https" if use_ssl else "http"
23
30
  self.endpoint = f"{scheme}://{host}:{port}"
24
31
  if use_ssl:
25
32
  credentials = grpc.ssl_channel_credentials()
26
- self.channel = grpc.aio.secure_channel(f"{host}:{port}", credentials)
33
+ self.channel = grpc.aio.secure_channel(
34
+ f"{host}:{port}", credentials, options=options
35
+ )
27
36
  else:
28
- self.channel = grpc.aio.insecure_channel(f"{host}:{port}")
37
+ self.channel = grpc.aio.insecure_channel(f"{host}:{port}", options=options)
29
38
 
30
39
  def set_jwt(self, jwt: str | None):
31
40
  self.jwt = jwt
@@ -13,6 +13,7 @@ from .. import definitions
13
13
 
14
14
  class TickersRequest(Struct, omit_defaults=True):
15
15
  i: Optional[Annotated[Optional[int], Meta(title="offset")]] = None
16
+ include_options: Optional[bool] = None
16
17
  k: Optional[
17
18
  Annotated[Optional[definitions.SortTickersBy], Meta(title="sort_by")]
18
19
  ] = None
@@ -25,6 +26,7 @@ class TickersRequest(Struct, omit_defaults=True):
25
26
  def new(
26
27
  cls,
27
28
  offset: Optional[int] = None,
29
+ include_options: Optional[bool] = None,
28
30
  sort_by: Optional[definitions.SortTickersBy] = None,
29
31
  limit: Optional[int] = None,
30
32
  symbols: Optional[List[str]] = None,
@@ -32,6 +34,7 @@ class TickersRequest(Struct, omit_defaults=True):
32
34
  ):
33
35
  return cls(
34
36
  offset,
37
+ include_options,
35
38
  sort_by,
36
39
  limit,
37
40
  symbols,
@@ -39,7 +42,7 @@ class TickersRequest(Struct, omit_defaults=True):
39
42
  )
40
43
 
41
44
  def __str__(self) -> str:
42
- return f"TickersRequest(offset={self.i},sort_by={self.k},limit={self.n},symbols={self.symbols},venue={self.venue})"
45
+ return f"TickersRequest(offset={self.i},include_options={self.include_options},sort_by={self.k},limit={self.n},symbols={self.symbols},venue={self.venue})"
43
46
 
44
47
  @property
45
48
  def offset(self) -> Optional[int]:
@@ -454,6 +454,7 @@ class OrderRejectReason(str, Enum):
454
454
  InsufficientCash = "InsufficientCash"
455
455
  InsufficientMargin = "InsufficientMargin"
456
456
  NotEasyToBorrow = "NotEasyToBorrow"
457
+ InvalidOrder = "InvalidOrder"
457
458
  Unknown = "Unknown"
458
459
 
459
460
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: architect-py
3
- Version: 5.1.3
3
+ Version: 5.1.4b1
4
4
  Summary: Python SDK for the Architect trading platform and brokerage.
5
5
  Author-email: "Architect Financial Technologies, Inc." <hello@architect.co>
6
6
  License-Expression: Apache-2.0
@@ -1,7 +1,7 @@
1
- architect_py/__init__.py,sha256=IA3WJIRKMNE_PPf4JFDfvwwcgS0692icwFM1ckFL8Fk,16121
2
- architect_py/async_client.py,sha256=IdR1vtC4eGRXKBUZ7A5bxaX6jh5jXPSgcHxl22dZ5eQ,61394
3
- architect_py/client.py,sha256=yC0OVzz6uXUMdIIhqqR3GyVuBApYSm00AS51QM8xPak,4911
4
- architect_py/client.pyi,sha256=VJrg85KaupKzWY4BvS86xREs67S_TFrItNirczYLrgc,24565
1
+ architect_py/__init__.py,sha256=8CrccOPnPzBy9EjbeXrvHImwdFvNofKqInTPsDz-9Fw,16123
2
+ architect_py/async_client.py,sha256=k6bGfqkan0sdlKI2LOYTRwwT-UZLPDgKagxo_IUqhxU,62227
3
+ architect_py/client.py,sha256=S9FsNoRJGB2xiE6H1nPPIg_-oAPZ5qPOBWy_4Hw0UuI,5056
4
+ architect_py/client.pyi,sha256=bXd7pjtc1lDCB0l6tRkHr8tydz7n65TUpsyUeYaqV_4,24801
5
5
  architect_py/common_types/__init__.py,sha256=fzOdIlKGWVN9V2Onc5z1v2bpvtZ4H9RSFA9ymJcBi3k,197
6
6
  architect_py/common_types/order_dir.py,sha256=ebyWTcXzJWrotkc2D9wNGc6JXbE5I3NLLuAz3I7FTZ8,2191
7
7
  architect_py/common_types/time_in_force.py,sha256=gEDYcNp014Eeb98zJDytiV0hGxHu_QsQndeM6Hk0Wa8,3132
@@ -23,12 +23,12 @@ architect_py/graphql_client/juniper_base_client.py,sha256=0kbAihyRgEP3n28zRumoST
23
23
  architect_py/graphql_client/search_symbols_query.py,sha256=hbGa6gF-gMWtRYQm2vlCTPDex8RWrJ4Yn4nT0VRQnCQ,614
24
24
  architect_py/graphql_client/user_id_query.py,sha256=tWKJJLgEINzd8e7rYlGklQCnwcwHzYFpCGQvhxQGX20,334
25
25
  architect_py/grpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
- architect_py/grpc/client.py,sha256=1_2JuxFoIVRtcuczkuI-uF7zy-AqSYehf5N3E9Gy4Jg,3406
26
+ architect_py/grpc/client.py,sha256=qApYInm_eEhSFKYZRQ_EJyrV-PMA1AOhJ1PoArzGGvM,3597
27
27
  architect_py/grpc/resolve_endpoint.py,sha256=r_PBWANIJJ47N5uyPcnefZ21ZE1-mzgACfCBfQpekg8,2621
28
28
  architect_py/grpc/server.py,sha256=Abmdfe1eYbctVgzoJYBBBLpd7UD70FbYQLtJImSyRzs,1934
29
29
  architect_py/grpc/utils.py,sha256=5sykLExUNZbcQHcxLCCM9DdOOiJJZcpputGrDtaMifY,667
30
30
  architect_py/grpc/models/__init__.py,sha256=RrTLZvU7mNykDNp1oOm4-dekzab9ugIXd_my7Sm0Vx4,9153
31
- architect_py/grpc/models/definitions.py,sha256=sGc8KYz4Hki6X3Kvr-Qe6iVnTaQC47wwJP3NeEPT5cQ,77014
31
+ architect_py/grpc/models/definitions.py,sha256=UB2-xXjoAM6ZTkNrsa1Q2-W_t6XUWHykP_R5tqzDcbc,77048
32
32
  architect_py/grpc/models/Accounts/AccountsRequest.py,sha256=1a88cltSebOb53EdJ0hKEGR7FlmBiibrCtGzLTKqDBY,1524
33
33
  architect_py/grpc/models/Accounts/AccountsResponse.py,sha256=DlXbkd3UbRybblBAfokw-K6nRvLNZgqz7cc0EKiW1zI,636
34
34
  architect_py/grpc/models/Accounts/__init__.py,sha256=sIyaEvJdP-VmGTGPPqZuRjKn4bc7NUClJ76Gd5uq-5s,57
@@ -108,7 +108,7 @@ architect_py/grpc/models/Marketdata/SubscribeTradesRequest.py,sha256=7P8FyNx6wij
108
108
  architect_py/grpc/models/Marketdata/Ticker.py,sha256=O4kJK1RyThYgfpvIr9mgRWAAkYgwwAKgOhEhbfDo9b4,11592
109
109
  architect_py/grpc/models/Marketdata/TickerRequest.py,sha256=Ay--5JKgCfdvlVWD2H6YSa_66NC3Dt6c-XK8JkbWhus,1008
110
110
  architect_py/grpc/models/Marketdata/TickerUpdate.py,sha256=sJ4wvCeGckMV30HwAcAsEMQbCzjN31OxF19q70jdxok,437
111
- architect_py/grpc/models/Marketdata/TickersRequest.py,sha256=_BYkOO2pk-terLNwyxN8gtHQxIrfPA7klodDeTS5ouM,2200
111
+ architect_py/grpc/models/Marketdata/TickersRequest.py,sha256=x6UlQdAisnXgs3vbghQY3nuiFccSU3ueU0YJVAKaKUs,2359
112
112
  architect_py/grpc/models/Marketdata/TickersResponse.py,sha256=CLzKx-ItwH9-Qq8YruFhXh7TmtHwzNRMEOPJ9LQD9co,574
113
113
  architect_py/grpc/models/Marketdata/Trade.py,sha256=RKZq_HUDLxIE41caDrwf99V0c48kH2pm3XRCn5RLcEQ,2476
114
114
  architect_py/grpc/models/Marketdata/__init__.py,sha256=sIyaEvJdP-VmGTGPPqZuRjKn4bc7NUClJ76Gd5uq-5s,57
@@ -170,7 +170,7 @@ architect_py/utils/orderbook.py,sha256=JM02NhHbmK3sNaS2Ara8FBY4TvKvtMIzJW1oVd8KC
170
170
  architect_py/utils/pandas.py,sha256=QHz2ynj4T92FobuzRaNoH3ypArHoSDCiGtZ3PVXJ2vo,1017
171
171
  architect_py/utils/price_bands.py,sha256=j7ioSA3dx025CD5E2Vg7XQvmjPvxQb-gzQBfQTovpTw,21874
172
172
  architect_py/utils/symbol_parsing.py,sha256=OjJzk2c6QU2s0aJMSyVEzlWD5Vy-RlakJVW7jNHVDJk,845
173
- architect_py-5.1.3.dist-info/licenses/LICENSE,sha256=6P0_5gYN8iPWPZeqA9nxiO3tRQmcSA1ijAVR7C8j1SI,11362
173
+ architect_py-5.1.4b1.dist-info/licenses/LICENSE,sha256=6P0_5gYN8iPWPZeqA9nxiO3tRQmcSA1ijAVR7C8j1SI,11362
174
174
  examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
175
175
  examples/book_subscription.py,sha256=1WFQN_QCE8cRS_CIv2k0NxqpK37fA9-Ja2Kfxs8vsb8,1461
176
176
  examples/candles.py,sha256=T71TsxbfXCT6mrJZmTgdTKesJFdQhYP_4AsiNK-8KyQ,756
@@ -187,14 +187,14 @@ examples/trades.py,sha256=0OzWQVTuIrC0KlisY0Tc-3pmWNP0m0-eCSntCV6Qdh8,561
187
187
  examples/tutorial_async.py,sha256=FNMjP2WmszRB0OXoMbshJ775LoPDzm55UZICa9ztr5w,2639
188
188
  examples/tutorial_sync.py,sha256=w5Sqa0YFh0XnpoXuhD3WsKRKpR5cuTTNb7pCp-Aqnz0,2846
189
189
  scripts/add_imports_to_inits.py,sha256=bryhz6RpKAJsSieVMnXnRyLp8evNkpOsNUkBUPkk1WQ,4518
190
- scripts/correct_sync_interface.py,sha256=O8qxSqNSNIL8KrgZ4C8rjs_pUCdcA1WeqKAggM2DINw,4056
190
+ scripts/correct_sync_interface.py,sha256=gTSJLDAT8s-ayN_JqgKbeM6c3DYZOapduS_GIqrvD-A,4134
191
191
  scripts/generate_functions_md.py,sha256=-rVRhbHlDodGH2a32UCsMLIpgXtDvOhBmkHa0RqDpCA,6232
192
192
  scripts/postprocess_grpc.py,sha256=QqFZdOLH6hLPRCLUkf7qvuGooLsXulcpMghCpleHc-A,23198
193
193
  scripts/preprocess_grpc_schema.py,sha256=p9LdoMZzixBSsVx7Dy3_8uJzOy_QwCoVMkAABQKUsBA,22894
194
194
  scripts/prune_graphql_schema.py,sha256=hmfw5FD_iKGKMFkq6H1neZiXXtljFFrOwi2fiusTWE4,6210
195
195
  templates/exceptions.py,sha256=tIHbiO5Q114h9nPwJXsgHvW_bERLwxuNp9Oj41p6t3A,2379
196
196
  templates/juniper_base_client.py,sha256=B8QF4IFSwqBK5UY2aFPbSdYnX9bcwnlxLK4ojPRaW0E,12705
197
- architect_py-5.1.3.dist-info/METADATA,sha256=Ozka0r2vOt_iWe4muYQRt8dLA--nNXXUeQSQ1Oi2S14,2638
198
- architect_py-5.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
199
- architect_py-5.1.3.dist-info/top_level.txt,sha256=UjtO97OACFQ9z5MzS-X2wBlt5Ovk1vxakQPKfokI454,40
200
- architect_py-5.1.3.dist-info/RECORD,,
197
+ architect_py-5.1.4b1.dist-info/METADATA,sha256=T9t4zgT065VEpW9f1WGsCXkUNK_O1UQimeeqfrem1uY,2640
198
+ architect_py-5.1.4b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
199
+ architect_py-5.1.4b1.dist-info/top_level.txt,sha256=UjtO97OACFQ9z5MzS-X2wBlt5Ovk1vxakQPKfokI454,40
200
+ architect_py-5.1.4b1.dist-info/RECORD,,
@@ -65,8 +65,11 @@ def add_correct_docstring_and_correct_the_init(content: str):
65
65
  param_parts = [str(p) for p in pos_params]
66
66
 
67
67
  # turn Optional[Type] into Type | None
68
- opt_re = re.compile(r"\b(?:Optional|Option)\[([^]]+)]")
69
- param_parts = [opt_re.sub(r"\1 | None", s) for s in param_parts]
68
+ opt_re = re.compile(r"\b(?:Optional|Option)\[(.+)]", re.DOTALL)
69
+ param_parts = [
70
+ opt_re.sub(lambda m: f"{m.group(1)} | None", s) # Optional[T] → T | None
71
+ for s in param_parts
72
+ ]
70
73
 
71
74
  param_str = ", ".join(param_parts)
72
75