architect-py 3.2.1__py3-none-any.whl → 3.2.2__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,2 +1,2 @@
1
- __version__ = "3.2.1"
1
+ __version__ = "3.2.2"
2
2
  # this is automatically updated by poetry-dynamic-versioning, see README.md
@@ -161,9 +161,9 @@ class AsyncClient:
161
161
  **kwargs: Any,
162
162
  ):
163
163
  """
164
- Users should not be using this constructor directly, unless they do not want to use any subscription methods.
164
+ Users should essentially never be using this constructor directly.
165
165
 
166
- Use the create method instead.
166
+ Use the connect method instead.
167
167
  See self.connect for arg explanations
168
168
  """
169
169
 
@@ -199,6 +199,14 @@ class AsyncClient:
199
199
  api_key=api_key, api_secret=api_secret, host=host, port=_port, **kwargs
200
200
  )
201
201
 
202
+ async def enable_orderflow(self):
203
+ """
204
+ Load and cache product and execution info so that the SDK can send orders.
205
+
206
+ CR alee: determine if this is better than @functools.lru_cache
207
+ """
208
+ pass
209
+
202
210
  # ------------------------------------------------------------
203
211
  # Symbology
204
212
  # ------------------------------------------------------------
@@ -244,7 +252,6 @@ class AsyncClient:
244
252
  info = await self.graphql_client.get_product_info_query(symbol)
245
253
  return info.product_info
246
254
 
247
- @functools.lru_cache
248
255
  async def get_product_infos(
249
256
  self, symbols: Optional[list[str]]
250
257
  ) -> Sequence[ProductInfoFields]:
@@ -264,7 +271,6 @@ class AsyncClient:
264
271
  infos = await self.graphql_client.get_product_infos_query(symbols)
265
272
  return infos.product_infos
266
273
 
267
- @functools.lru_cache
268
274
  async def get_execution_info(
269
275
  self, symbol: TradableProduct, execution_venue: str
270
276
  ) -> Optional[ExecutionInfoFields]:
@@ -949,6 +955,7 @@ class AsyncClient:
949
955
  async def send_limit_order(
950
956
  self,
951
957
  *,
958
+ id: Optional[str] = None,
952
959
  symbol: TradableProduct,
953
960
  execution_venue: Optional[str],
954
961
  odir: OrderDir,
@@ -1027,7 +1034,7 @@ class AsyncClient:
1027
1034
  quantity,
1028
1035
  order_type,
1029
1036
  time_in_force,
1030
- None,
1037
+ id,
1031
1038
  trader,
1032
1039
  account,
1033
1040
  limit_price,
@@ -1042,6 +1049,7 @@ class AsyncClient:
1042
1049
  async def send_market_pro_order(
1043
1050
  self,
1044
1051
  *,
1052
+ id: Optional[str] = None,
1045
1053
  symbol: TradableProduct,
1046
1054
  execution_venue: str,
1047
1055
  odir: OrderDir,
@@ -1118,6 +1126,7 @@ class AsyncClient:
1118
1126
  limit_price = tick_round_method(limit_price, tick_size)
1119
1127
 
1120
1128
  return await self.send_limit_order(
1129
+ id=id,
1121
1130
  symbol=symbol,
1122
1131
  execution_venue=execution_venue,
1123
1132
  odir=odir,
architect_py/client.py CHANGED
@@ -24,6 +24,8 @@ class Client(AsyncClientProtocol):
24
24
  This class is a wrapper around the AsyncClient class that allows you to call async methods synchronously.
25
25
  This does not work for subscription based methods.
26
26
 
27
+ This Client takes control of the event loop, which you can pass in.
28
+
27
29
  One can find the function definition in the AsyncClient class.
28
30
 
29
31
  The AsyncClient is more performant and powerful, so it is recommended to use that class if possible.
@@ -48,18 +50,6 @@ class Client(AsyncClientProtocol):
48
50
  loop: Optional[AbstractEventLoop] = None,
49
51
  **kwargs,
50
52
  ):
51
- super().__setattr__(
52
- "client",
53
- AsyncClient(
54
- api_key=api_key,
55
- api_secret=api_secret,
56
- host=host,
57
- paper_trading=paper_trading,
58
- _i_know_what_i_am_doing=True,
59
- **kwargs,
60
- ),
61
- )
62
-
63
53
  if loop is None:
64
54
  try:
65
55
  loop = asyncio.get_running_loop()
@@ -68,6 +58,20 @@ class Client(AsyncClientProtocol):
68
58
  asyncio.set_event_loop(loop)
69
59
  super().__setattr__("_loop", loop)
70
60
 
61
+ async_client = loop.run_until_complete(
62
+ AsyncClient.connect(
63
+ api_key=api_key,
64
+ api_secret=api_secret,
65
+ host=host,
66
+ paper_trading=paper_trading,
67
+ **kwargs,
68
+ )
69
+ )
70
+ super().__setattr__(
71
+ "client",
72
+ async_client,
73
+ )
74
+
71
75
  if "ipykernel" in sys.modules:
72
76
  # for jupyter notebooks
73
77
  import atexit
@@ -19,6 +19,7 @@ class AsyncClientProtocol:
19
19
  def cancel_order(self, order_id: str) -> CancelFields: ...
20
20
  @staticmethod
21
21
  def connect(*, api_key: str, api_secret: str, paper_trading: bool, host: str = 'app.architect.co', grpc_endpoint: str = 'cme.marketdata.architect.co', _port: Optional[int] = None, **kwargs: Any) -> AsyncClient: ...
22
+ def enable_orderflow(self) -> Any: ...
22
23
  def get_account_history(self, account: str, from_inclusive: Optional[datetime] = None, to_exclusive: Optional[datetime] = None) -> Sequence[AccountSummaryFields]: ...
23
24
  def get_account_summaries(self, accounts: Optional[list[str]] = None, trader: Optional[str] = None) -> Sequence[AccountSummaryFields]: ...
24
25
  def get_account_summary(self, account: str) -> AccountSummaryFields: ...
@@ -47,6 +48,6 @@ class AsyncClientProtocol:
47
48
  def get_product_infos(self, symbols: Optional[list[str]]) -> Sequence[ProductInfoFields]: ...
48
49
  def list_accounts(self) -> Sequence[AccountWithPermissionsFields]: ...
49
50
  def search_symbols(self, search_string: Optional[str] = None, execution_venue: Optional[str] = None, offset: int = 0, limit: int = 20) -> list[TradableProduct]: ...
50
- def send_limit_order(self, *, symbol: TradableProduct, execution_venue: Optional[str], odir: OrderDir, quantity: Decimal, limit_price: Decimal, order_type: OrderType = OrderType.LIMIT, time_in_force: TimeInForce = TimeInForce.DAY, good_til_date: Optional[datetime] = None, price_round_method: Optional[TickRoundMethod] = None, account: Optional[str] = None, trader: Optional[str] = None, post_only: bool = False, trigger_price: Optional[Decimal] = None) -> OrderFields: ...
51
- def send_market_pro_order(self, *, symbol: TradableProduct, execution_venue: str, odir: OrderDir, quantity: Decimal, time_in_force: TimeInForce = TimeInForce.DAY, account: Optional[str] = None, fraction_through_market: Decimal = Decimal('0.001')) -> OrderFields: ...
51
+ def send_limit_order(self, *, id: Optional[str] = None, symbol: TradableProduct, execution_venue: Optional[str], odir: OrderDir, quantity: Decimal, limit_price: Decimal, order_type: OrderType = OrderType.LIMIT, time_in_force: TimeInForce = TimeInForce.DAY, good_til_date: Optional[datetime] = None, price_round_method: Optional[TickRoundMethod] = None, account: Optional[str] = None, trader: Optional[str] = None, post_only: bool = False, trigger_price: Optional[Decimal] = None) -> OrderFields: ...
52
+ def send_market_pro_order(self, *, id: Optional[str] = None, symbol: TradableProduct, execution_venue: str, odir: OrderDir, quantity: Decimal, time_in_force: TimeInForce = TimeInForce.DAY, account: Optional[str] = None, fraction_through_market: Decimal = Decimal('0.001')) -> OrderFields: ...
52
53
  def who_am_i(self) -> tuple[str, str]: ...
@@ -6,7 +6,7 @@ from architect_py.grpc_client.Cpty.CptyResponse import (
6
6
  CptyResponse,
7
7
  Symbology,
8
8
  ReconcileOrder,
9
- ReconcileOpenOrder,
9
+ ReconcileOpenOrders,
10
10
  UpdateAccountSummary,
11
11
  )
12
12
 
@@ -60,8 +60,9 @@ CptyRequest = Annotated[
60
60
  ]
61
61
 
62
62
  CptyRequest_rpc_method = "duplex_stream"
63
+ UnannotatedCptyRequest = Login | Logout | PlaceOrder | CancelOrder
63
64
  CptyRequestResponseType = CptyResponse
64
65
  CptyRequestUnannotatedResponseType = (
65
- Symbology | ReconcileOrder | ReconcileOpenOrder | UpdateAccountSummary
66
+ Symbology | ReconcileOrder | ReconcileOpenOrders | UpdateAccountSummary
66
67
  )
67
68
  CptyRequest_route = "/json.architect.Cpty/Cpty"
@@ -63,7 +63,7 @@ class Symbology(Struct, omit_defaults=True, tag_field="t", tag="xs"):
63
63
  return f"Symbology(execution_info={self.execution_info})"
64
64
 
65
65
 
66
- class ReconcileOpenOrder(Struct, omit_defaults=True, tag_field="t", tag="oo"):
66
+ class ReconcileOpenOrders(Struct, omit_defaults=True, tag_field="t", tag="oo"):
67
67
  orders: List[Order]
68
68
  snapshot_for_account: Optional[definitions.AccountIdOrName] = None
69
69
 
@@ -80,7 +80,7 @@ class ReconcileOpenOrder(Struct, omit_defaults=True, tag_field="t", tag="oo"):
80
80
  )
81
81
 
82
82
  def __str__(self) -> str:
83
- return f"ReconcileOpenOrder(orders={self.orders},snapshot_for_account={self.snapshot_for_account})"
83
+ return f"ReconcileOpenOrders(orders={self.orders},snapshot_for_account={self.snapshot_for_account})"
84
84
 
85
85
 
86
86
  class ReconcileOrder(Order, omit_defaults=True, tag_field="t", tag="ro"):
@@ -88,6 +88,6 @@ class ReconcileOrder(Order, omit_defaults=True, tag_field="t", tag="ro"):
88
88
 
89
89
 
90
90
  CptyResponse = Annotated[
91
- Union[Symbology, ReconcileOrder, ReconcileOpenOrder, UpdateAccountSummary],
91
+ Union[Symbology, ReconcileOrder, ReconcileOpenOrders, UpdateAccountSummary],
92
92
  Meta(title="CptyResponse"),
93
93
  ]
@@ -0,0 +1,48 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: Cpty/CptyStatus.json
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Annotated, Optional
7
+
8
+ from msgspec import Meta, Struct
9
+
10
+
11
+ class CptyStatus(Struct, omit_defaults=True):
12
+ connected: bool
13
+ kind: str
14
+ last_heartbeat: Annotated[int, Meta(description="UNIX epoch time or -1 for never")]
15
+ """
16
+ UNIX epoch time or -1 for never
17
+ """
18
+ last_heartbeat_stale_threshold: Annotated[
19
+ int, Meta(description="Stale threshold in seconds, or -1 for never stale")
20
+ ]
21
+ """
22
+ Stale threshold in seconds, or -1 for never stale
23
+ """
24
+ logged_in: bool
25
+ instance: Optional[str] = None
26
+
27
+ # below is a constructor that takes all field titles as arguments for convenience
28
+ @classmethod
29
+ def new(
30
+ cls,
31
+ connected: bool,
32
+ kind: str,
33
+ last_heartbeat: int,
34
+ last_heartbeat_stale_threshold: int,
35
+ logged_in: bool,
36
+ instance: Optional[str] = None,
37
+ ):
38
+ return cls(
39
+ connected,
40
+ kind,
41
+ last_heartbeat,
42
+ last_heartbeat_stale_threshold,
43
+ logged_in,
44
+ instance,
45
+ )
46
+
47
+ def __str__(self) -> str:
48
+ return f"CptyStatus(connected={self.connected},kind={self.kind},last_heartbeat={self.last_heartbeat},last_heartbeat_stale_threshold={self.last_heartbeat_stale_threshold},logged_in={self.logged_in},instance={self.instance})"
@@ -0,0 +1,45 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: Cpty/CptyStatusRequest.json
3
+
4
+ from __future__ import annotations
5
+ from architect_py.grpc_client.Cpty.CptyStatus import CptyStatus
6
+
7
+ from typing import Optional
8
+
9
+ from msgspec import Struct
10
+
11
+
12
+ class CptyStatusRequest(Struct, omit_defaults=True):
13
+ kind: str
14
+ instance: Optional[str] = None
15
+
16
+ # below is a constructor that takes all field titles as arguments for convenience
17
+ @classmethod
18
+ def new(
19
+ cls,
20
+ kind: str,
21
+ instance: Optional[str] = None,
22
+ ):
23
+ return cls(
24
+ kind,
25
+ instance,
26
+ )
27
+
28
+ def __str__(self) -> str:
29
+ return f"CptyStatusRequest(kind={self.kind},instance={self.instance})"
30
+
31
+ @staticmethod
32
+ def get_response_type():
33
+ return CptyStatus
34
+
35
+ @staticmethod
36
+ def get_unannotated_response_type():
37
+ return CptyStatus
38
+
39
+ @staticmethod
40
+ def get_route() -> str:
41
+ return "/json.architect.Cpty/CptyStatus"
42
+
43
+ @staticmethod
44
+ def get_rpc_method():
45
+ return "unary"
@@ -0,0 +1,37 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: Cpty/CptysRequest.json
3
+
4
+ from __future__ import annotations
5
+ from architect_py.grpc_client.Cpty.CptysResponse import CptysResponse
6
+
7
+ from msgspec import Struct
8
+
9
+
10
+ class CptysRequest(Struct, omit_defaults=True):
11
+ pass
12
+
13
+ # below is a constructor that takes all field titles as arguments for convenience
14
+ @classmethod
15
+ def new(
16
+ cls,
17
+ ):
18
+ return cls()
19
+
20
+ def __str__(self) -> str:
21
+ return f"CptysRequest()"
22
+
23
+ @staticmethod
24
+ def get_response_type():
25
+ return CptysResponse
26
+
27
+ @staticmethod
28
+ def get_unannotated_response_type():
29
+ return CptysResponse
30
+
31
+ @staticmethod
32
+ def get_route() -> str:
33
+ return "/json.architect.Cpty/Cptys"
34
+
35
+ @staticmethod
36
+ def get_rpc_method():
37
+ return "unary"
@@ -0,0 +1,27 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: Cpty/CptysResponse.json
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import List
7
+
8
+ from msgspec import Struct
9
+
10
+ from .CptyStatus import CptyStatus
11
+
12
+
13
+ class CptysResponse(Struct, omit_defaults=True):
14
+ cptys: List[CptyStatus]
15
+
16
+ # below is a constructor that takes all field titles as arguments for convenience
17
+ @classmethod
18
+ def new(
19
+ cls,
20
+ cptys: List[CptyStatus],
21
+ ):
22
+ return cls(
23
+ cptys,
24
+ )
25
+
26
+ def __str__(self) -> str:
27
+ return f"CptysResponse(cptys={self.cptys})"
@@ -4,7 +4,7 @@
4
4
  from __future__ import annotations
5
5
  from datetime import datetime, timezone
6
6
 
7
- from datetime import datetime
7
+ from datetime import date, datetime
8
8
  from decimal import Decimal
9
9
  from typing import Annotated, Optional
10
10
 
@@ -37,6 +37,7 @@ class Ticker(Struct, omit_defaults=True):
37
37
  p: Optional[Annotated[Optional[Decimal], Meta(title="last_price")]] = None
38
38
  price_to_earnings: Optional[Decimal] = None
39
39
  q: Optional[Annotated[Optional[Decimal], Meta(title="last_size")]] = None
40
+ sd: Optional[Annotated[Optional[date], Meta(title="last_settlement_date")]] = None
40
41
  shares_outstanding_weighted_adj: Optional[Decimal] = None
41
42
  sp: Optional[Annotated[Optional[Decimal], Meta(title="last_settlement_price")]] = (
42
43
  None
@@ -75,6 +76,7 @@ class Ticker(Struct, omit_defaults=True):
75
76
  last_price: Optional[Decimal] = None,
76
77
  price_to_earnings: Optional[Decimal] = None,
77
78
  last_size: Optional[Decimal] = None,
79
+ last_settlement_date: Optional[date] = None,
78
80
  shares_outstanding_weighted_adj: Optional[Decimal] = None,
79
81
  last_settlement_price: Optional[Decimal] = None,
80
82
  volume_24h: Optional[Decimal] = None,
@@ -108,6 +110,7 @@ class Ticker(Struct, omit_defaults=True):
108
110
  last_price,
109
111
  price_to_earnings,
110
112
  last_size,
113
+ last_settlement_date,
111
114
  shares_outstanding_weighted_adj,
112
115
  last_settlement_price,
113
116
  volume_24h,
@@ -119,7 +122,7 @@ class Ticker(Struct, omit_defaults=True):
119
122
  )
120
123
 
121
124
  def __str__(self) -> str:
122
- return f"Ticker(symbol={self.s},timestamp_ns={self.tn},timestamp={self.ts},venue={self.ve},ask_price={self.ap},ask_size={self.as_},bid_price={self.bp},bid_size={self.bs},dividend={self.dividend},dividend_yield={self.dividend_yield},eps_adj={self.eps_adj},funding_rate={self.fr},next_funding_time={self.ft},high_24h={self.h},index_price={self.ip},low_24h={self.l},market_cap={self.market_cap},mark_price={self.mp},open_24h={self.o},open_interest={self.oi},last_price={self.p},price_to_earnings={self.price_to_earnings},last_size={self.q},shares_outstanding_weighted_adj={self.shares_outstanding_weighted_adj},last_settlement_price={self.sp},volume_24h={self.v},volume_30d={self.vm},session_high={self.xh},session_low={self.xl},session_open={self.xo},session_volume={self.xv})"
125
+ return f"Ticker(symbol={self.s},timestamp_ns={self.tn},timestamp={self.ts},venue={self.ve},ask_price={self.ap},ask_size={self.as_},bid_price={self.bp},bid_size={self.bs},dividend={self.dividend},dividend_yield={self.dividend_yield},eps_adj={self.eps_adj},funding_rate={self.fr},next_funding_time={self.ft},high_24h={self.h},index_price={self.ip},low_24h={self.l},market_cap={self.market_cap},mark_price={self.mp},open_24h={self.o},open_interest={self.oi},last_price={self.p},price_to_earnings={self.price_to_earnings},last_size={self.q},last_settlement_date={self.sd},shares_outstanding_weighted_adj={self.shares_outstanding_weighted_adj},last_settlement_price={self.sp},volume_24h={self.v},volume_30d={self.vm},session_high={self.xh},session_low={self.xl},session_open={self.xo},session_volume={self.xv})"
123
126
 
124
127
  @property
125
128
  def symbol(self) -> str:
@@ -273,6 +276,14 @@ class Ticker(Struct, omit_defaults=True):
273
276
  def last_size(self, value: Optional[Decimal]) -> None:
274
277
  self.q = value
275
278
 
279
+ @property
280
+ def last_settlement_date(self) -> Optional[date]:
281
+ return self.sd
282
+
283
+ @last_settlement_date.setter
284
+ def last_settlement_date(self, value: Optional[date]) -> None:
285
+ self.sd = value
286
+
276
287
  @property
277
288
  def last_settlement_price(self) -> Optional[Decimal]:
278
289
  return self.sp
@@ -46,6 +46,7 @@ OrderflowRequest = Annotated[
46
46
  ]
47
47
 
48
48
  OrderflowRequest_rpc_method = "duplex_stream"
49
+ UnannotatedOrderflowRequest = PlaceOrder | CancelOrder | CancelAllOrders
49
50
  OrderflowRequestResponseType = Orderflow
50
51
  OrderflowRequestUnannotatedResponseType = (
51
52
  OrderPending