architect-py 5.1.4rc1__py3-none-any.whl → 5.1.6__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 (49) hide show
  1. architect_py/__init__.py +24 -4
  2. architect_py/async_client.py +121 -67
  3. architect_py/client.pyi +16 -37
  4. architect_py/grpc/models/Accounts/ResetPaperAccountRequest.py +59 -0
  5. architect_py/grpc/models/Accounts/ResetPaperAccountResponse.py +20 -0
  6. architect_py/grpc/models/Boss/OptionsTransactionsRequest.py +42 -0
  7. architect_py/grpc/models/Boss/OptionsTransactionsResponse.py +27 -0
  8. architect_py/grpc/models/Core/ConfigResponse.py +7 -2
  9. architect_py/grpc/models/Folio/AccountSummary.py +7 -1
  10. architect_py/grpc/models/Marketdata/Candle.py +6 -0
  11. architect_py/grpc/models/Marketdata/L1BookSnapshot.py +6 -0
  12. architect_py/grpc/models/Marketdata/L2BookSnapshot.py +6 -0
  13. architect_py/grpc/models/Marketdata/Liquidation.py +6 -0
  14. architect_py/grpc/models/Marketdata/Ticker.py +6 -0
  15. architect_py/grpc/models/Marketdata/Trade.py +6 -0
  16. architect_py/grpc/models/OptionsMarketdata/OptionsChain.py +5 -5
  17. architect_py/grpc/models/OptionsMarketdata/OptionsChainGreeks.py +5 -5
  18. architect_py/grpc/models/OptionsMarketdata/OptionsChainGreeksRequest.py +5 -1
  19. architect_py/grpc/models/OptionsMarketdata/OptionsChainRequest.py +5 -1
  20. architect_py/grpc/models/OptionsMarketdata/OptionsContract.py +45 -0
  21. architect_py/grpc/models/OptionsMarketdata/OptionsContractGreeksRequest.py +40 -0
  22. architect_py/grpc/models/OptionsMarketdata/OptionsContractRequest.py +40 -0
  23. architect_py/grpc/models/OptionsMarketdata/OptionsExpirations.py +4 -1
  24. architect_py/grpc/models/OptionsMarketdata/OptionsExpirationsRequest.py +8 -1
  25. architect_py/grpc/models/OptionsMarketdata/OptionsGreeks.py +58 -0
  26. architect_py/grpc/models/OptionsMarketdata/OptionsWraps.py +28 -0
  27. architect_py/grpc/models/OptionsMarketdata/OptionsWrapsRequest.py +40 -0
  28. architect_py/grpc/models/__init__.py +11 -1
  29. architect_py/grpc/models/definitions.py +57 -86
  30. architect_py/grpc/orderflow.py +3 -7
  31. architect_py/grpc/server.py +1 -3
  32. architect_py/tests/test_order_entry.py +120 -1
  33. architect_py/tests/test_positions.py +364 -0
  34. architect_py/tests/test_rounding.py +28 -28
  35. architect_py/utils/pandas.py +50 -1
  36. {architect_py-5.1.4rc1.dist-info → architect_py-5.1.6.dist-info}/METADATA +1 -1
  37. {architect_py-5.1.4rc1.dist-info → architect_py-5.1.6.dist-info}/RECORD +49 -38
  38. examples/external_cpty.py +2 -1
  39. examples/funding_rate_mean_reversion_algo.py +4 -4
  40. examples/order_sending.py +3 -3
  41. examples/orderflow_channel.py +75 -56
  42. examples/stream_l1_marketdata.py +3 -1
  43. examples/stream_l2_marketdata.py +3 -1
  44. examples/tutorial_async.py +3 -2
  45. examples/tutorial_sync.py +4 -3
  46. scripts/generate_functions_md.py +2 -1
  47. {architect_py-5.1.4rc1.dist-info → architect_py-5.1.6.dist-info}/WHEEL +0 -0
  48. {architect_py-5.1.4rc1.dist-info → architect_py-5.1.6.dist-info}/licenses/LICENSE +0 -0
  49. {architect_py-5.1.4rc1.dist-info → architect_py-5.1.6.dist-info}/top_level.txt +0 -0
architect_py/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # ruff: noqa:I001
2
2
 
3
- __version__ = "5.1.4rc1"
3
+ __version__ = "5.1.6"
4
4
 
5
5
  from .utils.nearest_tick import TickRoundMethod
6
6
  from .async_client import AsyncClient
@@ -21,6 +21,7 @@ from .grpc.models.definitions import (
21
21
  HealthMetric,
22
22
  HealthStatus,
23
23
  L2BookDiff,
24
+ OptionsTransaction,
24
25
  OrderId,
25
26
  OrderOut,
26
27
  OrderRejectReason,
@@ -29,6 +30,7 @@ from .grpc.models.definitions import (
29
30
  OrderStatus,
30
31
  OrderType,
31
32
  ProductCatalogInfo,
33
+ PutOrCall,
32
34
  RqdAccountStatistics,
33
35
  SortTickersBy,
34
36
  Statement,
@@ -53,7 +55,6 @@ from .grpc.models.definitions import (
53
55
  Future,
54
56
  Perpetual,
55
57
  Unknown,
56
- PutOrCall,
57
58
  SnapshotOrUpdateForStringAndProductCatalogInfo1,
58
59
  SnapshotOrUpdateForStringAndProductCatalogInfo2,
59
60
  SnapshotOrUpdateForStringAndString1,
@@ -73,8 +74,6 @@ from .grpc.models.definitions import (
73
74
  CptyLoginRequest,
74
75
  ExecutionInfo,
75
76
  Fill,
76
- OptionsContract,
77
- OptionsGreeks,
78
77
  OptionsSeriesInfo,
79
78
  OrderAck,
80
79
  OrderCanceled,
@@ -105,6 +104,8 @@ from .grpc.models.definitions import (
105
104
  )
106
105
  from .grpc.models.Accounts.AccountsRequest import AccountsRequest
107
106
  from .grpc.models.Accounts.AccountsResponse import AccountsResponse
107
+ from .grpc.models.Accounts.ResetPaperAccountRequest import ResetPaperAccountRequest
108
+ from .grpc.models.Accounts.ResetPaperAccountResponse import ResetPaperAccountResponse
108
109
  from .grpc.models.Algo.AlgoOrder import AlgoOrder
109
110
  from .grpc.models.Algo.AlgoOrderRequest import AlgoOrderRequest
110
111
  from .grpc.models.Algo.AlgoOrdersRequest import AlgoOrdersRequest
@@ -123,6 +124,8 @@ from .grpc.models.Auth.CreateJwtRequest import CreateJwtRequest
123
124
  from .grpc.models.Auth.CreateJwtResponse import CreateJwtResponse
124
125
  from .grpc.models.Boss.DepositsRequest import DepositsRequest
125
126
  from .grpc.models.Boss.DepositsResponse import DepositsResponse
127
+ from .grpc.models.Boss.OptionsTransactionsRequest import OptionsTransactionsRequest
128
+ from .grpc.models.Boss.OptionsTransactionsResponse import OptionsTransactionsResponse
126
129
  from .grpc.models.Boss.RqdAccountStatisticsRequest import RqdAccountStatisticsRequest
127
130
  from .grpc.models.Boss.RqdAccountStatisticsResponse import RqdAccountStatisticsResponse
128
131
  from .grpc.models.Boss.StatementUrlRequest import StatementUrlRequest
@@ -206,10 +209,18 @@ from .grpc.models.OptionsMarketdata.OptionsChainGreeksRequest import (
206
209
  OptionsChainGreeksRequest,
207
210
  )
208
211
  from .grpc.models.OptionsMarketdata.OptionsChainRequest import OptionsChainRequest
212
+ from .grpc.models.OptionsMarketdata.OptionsContract import OptionsContract
213
+ from .grpc.models.OptionsMarketdata.OptionsContractGreeksRequest import (
214
+ OptionsContractGreeksRequest,
215
+ )
216
+ from .grpc.models.OptionsMarketdata.OptionsContractRequest import OptionsContractRequest
209
217
  from .grpc.models.OptionsMarketdata.OptionsExpirations import OptionsExpirations
210
218
  from .grpc.models.OptionsMarketdata.OptionsExpirationsRequest import (
211
219
  OptionsExpirationsRequest,
212
220
  )
221
+ from .grpc.models.OptionsMarketdata.OptionsGreeks import OptionsGreeks
222
+ from .grpc.models.OptionsMarketdata.OptionsWraps import OptionsWraps
223
+ from .grpc.models.OptionsMarketdata.OptionsWrapsRequest import OptionsWrapsRequest
213
224
  from .grpc.models.Orderflow.Dropcopy import Dropcopy
214
225
  from .grpc.models.Orderflow.DropcopyRequest import DropcopyRequest
215
226
  from .grpc.models.Orderflow.Orderflow import Orderflow
@@ -350,12 +361,19 @@ __all__ = [
350
361
  "OptionsChainGreeksRequest",
351
362
  "OptionsChainRequest",
352
363
  "OptionsContract",
364
+ "OptionsContractGreeksRequest",
365
+ "OptionsContractRequest",
353
366
  "OptionsExerciseType",
354
367
  "OptionsExpirations",
355
368
  "OptionsExpirationsRequest",
356
369
  "OptionsGreeks",
357
370
  "OptionsSeriesInfo",
358
371
  "OptionsSeriesInstance",
372
+ "OptionsTransaction",
373
+ "OptionsTransactionsRequest",
374
+ "OptionsTransactionsResponse",
375
+ "OptionsWraps",
376
+ "OptionsWrapsRequest",
359
377
  "Order",
360
378
  "OrderAck",
361
379
  "OrderCanceled",
@@ -384,6 +402,8 @@ __all__ = [
384
402
  "PruneExpiredSymbolsRequest",
385
403
  "PruneExpiredSymbolsResponse",
386
404
  "PutOrCall",
405
+ "ResetPaperAccountRequest",
406
+ "ResetPaperAccountResponse",
387
407
  "RestartCptyRequest",
388
408
  "RestartCptyResponse",
389
409
  "RqdAccountStatistics",
@@ -44,7 +44,7 @@ from architect_py.grpc.orderflow import OrderflowChannel
44
44
  from architect_py.grpc.resolve_endpoint import PAPER_GRPC_PORT, resolve_endpoint
45
45
  from architect_py.utils.nearest_tick import TickRoundMethod
46
46
  from architect_py.utils.orderbook import update_orderbook_side
47
- from architect_py.utils.pandas import candles_to_dataframe
47
+ from architect_py.utils.pandas import candles_to_dataframe, tickers_to_dataframe
48
48
  from architect_py.utils.price_bands import price_band_pairs
49
49
  from architect_py.utils.symbol_parsing import nominative_expiration
50
50
 
@@ -146,7 +146,7 @@ class AsyncClient:
146
146
  await client.refresh_jwt()
147
147
 
148
148
  logging.info("Discovering marketdata endpoints...")
149
- await client.discover_marketdata()
149
+ await client._discover_marketdata()
150
150
 
151
151
  return client
152
152
 
@@ -260,7 +260,7 @@ class AsyncClient:
260
260
  except Exception as e:
261
261
  logging.error("Failed to refresh gRPC credentials: %s", e)
262
262
 
263
- def set_jwt(self, jwt: str | None, jwt_expiration: datetime | None = None):
263
+ def _set_jwt(self, jwt: str | None, jwt_expiration: datetime | None = None):
264
264
  """
265
265
  Manually set the JWT for gRPC authentication.
266
266
 
@@ -272,18 +272,18 @@ class AsyncClient:
272
272
  self.jwt = jwt
273
273
  self.jwt_expiration = jwt_expiration
274
274
 
275
- async def discover_marketdata(self):
275
+ async def _discover_marketdata(self):
276
276
  """
277
277
  Load marketdata endpoints from the server config.
278
278
 
279
279
  The Architect core is responsible for telling you where to find marketdata as per
280
280
  its configuration. You can also manually set marketdata endpoints by calling
281
- set_marketdata directly.
281
+ _set_marketdata directly.
282
282
 
283
283
  This method is called on AsyncClient.connect.
284
284
  """
285
285
  try:
286
- grpc_client = await self.core()
286
+ grpc_client = await self._core()
287
287
  req = ConfigRequest()
288
288
  res: ConfigResponse = await grpc_client.unary_unary(req)
289
289
  for venue, endpoint in res.marketdata.items():
@@ -329,7 +329,7 @@ class AsyncClient:
329
329
  except Exception as e:
330
330
  logging.error("Failed to set marketdata endpoint: %s", e)
331
331
 
332
- async def marketdata(self, venue: Venue) -> GrpcClient:
332
+ async def _marketdata(self, venue: Venue) -> GrpcClient:
333
333
  """
334
334
  Get the marketdata client for a venue.
335
335
  """
@@ -364,7 +364,7 @@ class AsyncClient:
364
364
  except Exception as e:
365
365
  logging.error("Failed to set hmart endpoint: %s", e)
366
366
 
367
- async def hmart(self) -> GrpcClient:
367
+ async def _hmart(self) -> GrpcClient:
368
368
  """
369
369
  Get the hmart (historical marketdata service) client.
370
370
  """
@@ -379,7 +379,7 @@ class AsyncClient:
379
379
  self.grpc_hmart.set_jwt(self.jwt)
380
380
  return self.grpc_hmart
381
381
 
382
- async def core(self) -> GrpcClient:
382
+ async def _core(self) -> GrpcClient:
383
383
  """
384
384
  Get the core client.
385
385
  """
@@ -400,8 +400,11 @@ class AsyncClient:
400
400
  res = await self.graphql_client.user_id_query()
401
401
  return res.user_id, res.user_email
402
402
 
403
- async def auth_info(self) -> AuthInfoResponse:
404
- grpc_client = await self.core()
403
+ async def _auth_info(self) -> AuthInfoResponse:
404
+ """
405
+ Gets auth info mapping
406
+ """
407
+ grpc_client = await self._core()
405
408
  req = AuthInfoRequest()
406
409
  res: AuthInfoResponse = await grpc_client.unary_unary(req)
407
410
  return res
@@ -420,7 +423,7 @@ class AsyncClient:
420
423
  """
421
424
  Get cpty status.
422
425
  """
423
- grpc_client = await self.core()
426
+ grpc_client = await self._core()
424
427
  req = CptyStatusRequest(kind=kind, instance=instance)
425
428
  res: CptyStatus = await grpc_client.unary_unary(req)
426
429
  return res
@@ -440,9 +443,9 @@ class AsyncClient:
440
443
  cross-referencing symbols or checking availability.
441
444
  """
442
445
  if marketdata is not None:
443
- grpc_client = await self.marketdata(marketdata)
446
+ grpc_client = await self._marketdata(marketdata)
444
447
  else:
445
- grpc_client = await self.core()
448
+ grpc_client = await self._core()
446
449
  req = SymbolsRequest()
447
450
  res: SymbolsResponse = await grpc_client.unary_unary(req)
448
451
  return res.symbols
@@ -487,6 +490,20 @@ class AsyncClient:
487
490
  None if the symbol does not exist
488
491
  """
489
492
  res = await self.graphql_client.get_product_info_query(symbol)
493
+ if res.product_info is None:
494
+ if "/" in symbol:
495
+ assert ValueError(
496
+ f"Product info not found for symbol: {symbol}.\n"
497
+ "for calling get_product_info, "
498
+ f"symbol {symbol} should not have a quote (ie should not end with /USD);"
499
+ "either use the base() method of TradableProduct, or remove the quote from the symbol"
500
+ )
501
+ raise ValueError(
502
+ f"Product info not found for symbol: {symbol}."
503
+ "Please ensure it is of the form 'ES 20250620 CME Future' or 'AAPL US Equity'."
504
+ "(note that Future and Equity are not completely capitalized)."
505
+ )
506
+
490
507
  return res.product_info
491
508
 
492
509
  async def get_product_infos(
@@ -629,7 +646,7 @@ class AsyncClient:
629
646
  futures.sort()
630
647
  return TradableProduct(futures[0], "USD")
631
648
  else:
632
- grpc_client = await self.marketdata(venue)
649
+ grpc_client = await self._marketdata(venue)
633
650
  req = TickersRequest(
634
651
  symbols=[TradableProduct(f"{future}/USD") for future in futures],
635
652
  k=SortTickersBy.VOLUME_DESC,
@@ -723,7 +740,7 @@ class AsyncClient:
723
740
  symbol: the symbol to get the market status for, e.g. "ES 20250321 CME Future/USD"
724
741
  venue: the venue that the symbol is traded at, e.g. CME
725
742
  """
726
- grpc_client = await self.marketdata(venue)
743
+ grpc_client = await self._marketdata(venue)
727
744
  req = MarketStatusRequest(symbol=str(symbol), venue=venue)
728
745
  res: MarketStatus = await grpc_client.unary_unary(req)
729
746
  return res
@@ -805,25 +822,17 @@ class AsyncClient:
805
822
  as_dataframe: if True, return a pandas DataFrame
806
823
 
807
824
  """
808
- grpc_client = await self.hmart()
809
- if start.tzinfo is not timezone.utc:
810
- raise ValueError(
811
- "start must be a utc datetime:\n"
812
- "for example datetime.now(timezone.utc) or \n"
813
- "dt = datetime(2025, 4, 15, 12, 0, 0, tzinfo=timezone.utc)"
814
- )
815
- if end.tzinfo is not timezone.utc:
816
- raise ValueError(
817
- "end must be a utc datetime:\n"
818
- "for example datetime.now(timezone.utc) or \n"
819
- "dt = datetime(2025, 4, 15, 12, 0, 0, tzinfo=timezone.utc)"
820
- )
825
+ grpc_client = await self._hmart()
826
+ if not start.tzinfo:
827
+ raise ValueError("start time must be timezone-aware")
828
+ if not end.tzinfo:
829
+ raise ValueError("end time must be timezone-aware")
821
830
  req = HistoricalCandlesRequest(
822
831
  venue=venue,
823
832
  symbol=str(symbol),
824
833
  candle_width=candle_width,
825
- start_date=start,
826
- end_date=end,
834
+ start_date=start.astimezone(timezone.utc),
835
+ end_date=end.astimezone(timezone.utc),
827
836
  )
828
837
  res: HistoricalCandlesResponse = await grpc_client.unary_unary(req)
829
838
 
@@ -844,7 +853,7 @@ class AsyncClient:
844
853
  symbol: the symbol to get the l1 book snapshot for
845
854
  venue: the venue that the symbol is traded at
846
855
  """
847
- grpc_client = await self.marketdata(venue)
856
+ grpc_client = await self._marketdata(venue)
848
857
  req = L1BookSnapshotRequest(symbol=str(symbol), venue=venue)
849
858
  res: L1BookSnapshot = await grpc_client.unary_unary(req)
850
859
  return res
@@ -859,7 +868,7 @@ class AsyncClient:
859
868
  symbols: the symbols to get the l1 book snapshots for
860
869
  venue: the venue that the symbols are traded at
861
870
  """
862
- grpc_client = await self.marketdata(venue)
871
+ grpc_client = await self._marketdata(venue)
863
872
  req = L1BookSnapshotsRequest(symbols=symbols)
864
873
  res: ArrayOfL1BookSnapshot = await grpc_client.unary_unary(
865
874
  req # pyright: ignore[reportArgumentType]
@@ -876,7 +885,7 @@ class AsyncClient:
876
885
  symbol: the symbol to get the l2 book snapshot for
877
886
  venue: the venue that the symbol is traded at
878
887
  """
879
- grpc_client = await self.marketdata(venue)
888
+ grpc_client = await self._marketdata(venue)
880
889
  req = L2BookSnapshotRequest(symbol=str(symbol), venue=venue)
881
890
  res: L2BookSnapshot = await grpc_client.unary_unary(req)
882
891
  return res
@@ -885,11 +894,42 @@ class AsyncClient:
885
894
  """
886
895
  Gets the ticker for a symbol.
887
896
  """
888
- grpc_client = await self.marketdata(venue)
897
+ grpc_client = await self._marketdata(venue)
889
898
  req = TickerRequest(symbol=str(symbol), venue=venue)
890
899
  res: Ticker = await grpc_client.unary_unary(req)
891
900
  return res
892
901
 
902
+ async def get_tickers(
903
+ self,
904
+ *,
905
+ venue: Venue,
906
+ symbols: Optional[Sequence[TradableProduct | str]] = None,
907
+ include_options: bool = False,
908
+ sort_by: Optional[SortTickersBy | str] = None,
909
+ offset: Optional[int] = None,
910
+ limit: Optional[int] = None,
911
+ as_dataframe: bool = False,
912
+ ) -> Union[Sequence[Ticker], pd.DataFrame]:
913
+ """
914
+ Gets the tickers for a list of symbols.
915
+ """
916
+ grpc_client = await self._marketdata(venue)
917
+ sort_by = SortTickersBy(sort_by) if sort_by else None
918
+ symbols = [str(symbol) for symbol in symbols] if symbols else None
919
+ req = TickersRequest.new(
920
+ offset=offset,
921
+ include_options=include_options,
922
+ sort_by=sort_by,
923
+ limit=limit,
924
+ symbols=symbols,
925
+ venue=venue,
926
+ )
927
+ res: TickersResponse = await grpc_client.unary_unary(req)
928
+ if as_dataframe:
929
+ return tickers_to_dataframe(res.tickers)
930
+ else:
931
+ return res.tickers
932
+
893
933
  async def stream_l1_book_snapshots(
894
934
  self,
895
935
  symbols: Sequence[TradableProduct | str],
@@ -905,7 +945,7 @@ class AsyncClient:
905
945
  If symbols=None, subscribe to all symbols available for the venue.
906
946
  venue: the venue to subscribe to
907
947
  """
908
- grpc_client = await self.marketdata(venue)
948
+ grpc_client = await self._marketdata(venue)
909
949
  req = SubscribeL1BookSnapshotsRequest(
910
950
  symbols=list(symbols),
911
951
  venue=venue,
@@ -927,7 +967,7 @@ class AsyncClient:
927
967
  symbol: the symbol to subscribe to
928
968
  venue: the marketdata venue
929
969
  """
930
- grpc_client = await self.marketdata(venue)
970
+ grpc_client = await self._marketdata(venue)
931
971
  req = SubscribeL2BookUpdatesRequest(symbol=str(symbol), venue=venue)
932
972
  async for res in grpc_client.unary_stream(
933
973
  req # pyright: ignore[reportArgumentType]
@@ -960,7 +1000,7 @@ class AsyncClient:
960
1000
  else:
961
1001
  self.l1_books[venue] = {}
962
1002
 
963
- grpc_client = await self.marketdata(venue)
1003
+ grpc_client = await self._marketdata(venue)
964
1004
  book = L1BookSnapshot(symbol, 0, 0)
965
1005
  self.l1_books[venue][symbol] = (
966
1006
  book,
@@ -1038,7 +1078,7 @@ class AsyncClient:
1038
1078
  else:
1039
1079
  self.l2_books[venue] = {}
1040
1080
 
1041
- grpc_client = await self.marketdata(venue)
1081
+ grpc_client = await self._marketdata(venue)
1042
1082
  book = L2BookSnapshot([], [], 0, 0, 0, 0)
1043
1083
  self.l2_books[venue][symbol] = (
1044
1084
  book,
@@ -1097,7 +1137,7 @@ class AsyncClient:
1097
1137
  """
1098
1138
  Subscribe to a stream of trades for a symbol.
1099
1139
  """
1100
- grpc_client = await self.marketdata(venue)
1140
+ grpc_client = await self._marketdata(venue)
1101
1141
  req = SubscribeTradesRequest(symbol=str(symbol), venue=venue)
1102
1142
  async for res in grpc_client.unary_stream(req):
1103
1143
  yield res
@@ -1111,7 +1151,7 @@ class AsyncClient:
1111
1151
  """
1112
1152
  Subscribe to a stream of candles for a symbol.
1113
1153
  """
1114
- grpc_client = await self.marketdata(venue)
1154
+ grpc_client = await self._marketdata(venue)
1115
1155
  req = SubscribeCandlesRequest(
1116
1156
  symbol=str(symbol),
1117
1157
  venue=venue,
@@ -1133,7 +1173,7 @@ class AsyncClient:
1133
1173
  a list of AccountWithPermissions for the user that the API key belongs to
1134
1174
  (use who_am_i to get the user_id / email)
1135
1175
  """
1136
- grpc_client = await self.core()
1176
+ grpc_client = await self._core()
1137
1177
  req = AccountsRequest(paper=self.paper_trading)
1138
1178
  res = await grpc_client.unary_unary(req)
1139
1179
  return res.accounts
@@ -1146,11 +1186,32 @@ class AsyncClient:
1146
1186
  account: account uuid or name
1147
1187
  Examples: "00000000-0000-0000-0000-000000000000", "STONEX:000000/JDoe"
1148
1188
  """
1149
- grpc_client = await self.core()
1189
+ grpc_client = await self._core()
1150
1190
  req = AccountSummaryRequest(account=account)
1151
1191
  res = await grpc_client.unary_unary(req)
1152
1192
  return res
1153
1193
 
1194
+ async def get_positions(
1195
+ self,
1196
+ accounts: Optional[list[str]] = None,
1197
+ trader: Optional[str] = None,
1198
+ ) -> dict[str, Decimal]:
1199
+ """
1200
+ Get positions for the specified symbols.
1201
+
1202
+ Args:
1203
+ symbols: list of symbol strings
1204
+ """
1205
+ account_summaries = await self.get_account_summaries(
1206
+ accounts=accounts, trader=trader
1207
+ )
1208
+ positions: dict[str, Decimal] = {}
1209
+ for summary in account_summaries:
1210
+ for symbol, summary in summary.positions.items():
1211
+ for pos in summary:
1212
+ positions[symbol] = positions.get(symbol, Decimal(0)) + pos.quantity
1213
+ return positions
1214
+
1154
1215
  async def get_account_summaries(
1155
1216
  self,
1156
1217
  accounts: Optional[list[str]] = None,
@@ -1165,7 +1226,7 @@ class AsyncClient:
1165
1226
 
1166
1227
  If both arguments are given, the union of matching accounts are returned.
1167
1228
  """
1168
- grpc_client = await self.core()
1229
+ grpc_client = await self._core()
1169
1230
  request = AccountSummariesRequest(
1170
1231
  accounts=accounts,
1171
1232
  trader=trader,
@@ -1182,7 +1243,7 @@ class AsyncClient:
1182
1243
  """
1183
1244
  Get historical sequence of account summaries for the given account.
1184
1245
  """
1185
- grpc_client = await self.core()
1246
+ grpc_client = await self._core()
1186
1247
  if from_inclusive is not None:
1187
1248
  assert from_inclusive.tzinfo is timezone.utc, (
1188
1249
  "from_inclusive must be a utc datetime:\n"
@@ -1230,7 +1291,7 @@ class AsyncClient:
1230
1291
  Returns:
1231
1292
  Open orders that match the union of the filters
1232
1293
  """
1233
- grpc_client = await self.core()
1294
+ grpc_client = await self._core()
1234
1295
  open_orders_request = OpenOrdersRequest(
1235
1296
  venue=venue,
1236
1297
  account=account,
@@ -1279,7 +1340,7 @@ class AsyncClient:
1279
1340
  If order_ids is not specified, then from_inclusive and to_exclusive
1280
1341
  MUST be specified.
1281
1342
  """
1282
- grpc_client = await self.core()
1343
+ grpc_client = await self._core()
1283
1344
 
1284
1345
  if from_inclusive is not None:
1285
1346
  assert from_inclusive.tzinfo is timezone.utc, (
@@ -1320,7 +1381,7 @@ class AsyncClient:
1320
1381
  Args:
1321
1382
  order_id: the order id to get
1322
1383
  """
1323
- grpc_client = await self.core()
1384
+ grpc_client = await self._core()
1324
1385
  req = OpenOrdersRequest.new(
1325
1386
  order_ids=[order_id],
1326
1387
  )
@@ -1344,7 +1405,7 @@ class AsyncClient:
1344
1405
  Args:
1345
1406
  order_ids: a list of order ids to get
1346
1407
  """
1347
- grpc_client = await self.core()
1408
+ grpc_client = await self._core()
1348
1409
  orders_dict: dict[OrderId, Optional[Order]] = {
1349
1410
  order_id: None for order_id in order_ids
1350
1411
  }
@@ -1388,7 +1449,7 @@ class AsyncClient:
1388
1449
  account: account uuid or name
1389
1450
  order_id: the order id to get fills for
1390
1451
  """
1391
- grpc_client = await self.core()
1452
+ grpc_client = await self._core()
1392
1453
  if from_inclusive is not None:
1393
1454
  assert from_inclusive.tzinfo is timezone.utc, (
1394
1455
  "from_inclusive must be a utc datetime:\n"
@@ -1442,7 +1503,7 @@ class AsyncClient:
1442
1503
  print(event)
1443
1504
  ```
1444
1505
  """
1445
- grpc_client = await self.core()
1506
+ grpc_client = await self._core()
1446
1507
  req: SubscribeOrderflowRequest = SubscribeOrderflowRequest(
1447
1508
  account=account, execution_venue=execution_venue, trader=trader
1448
1509
  )
@@ -1512,7 +1573,7 @@ class AsyncClient:
1512
1573
  trigger_price=trigger_price,
1513
1574
  )
1514
1575
  """
1515
- grpc_client = await self.core()
1576
+ grpc_client = await self._core()
1516
1577
 
1517
1578
  res = await asyncio.gather(
1518
1579
  *[
@@ -1531,7 +1592,7 @@ class AsyncClient:
1531
1592
  execution_venue: Optional[str] = None,
1532
1593
  dir: OrderDir,
1533
1594
  quantity: Decimal,
1534
- limit_price: Decimal,
1595
+ limit_price: Optional[Decimal] = None,
1535
1596
  order_type: OrderType = OrderType.LIMIT,
1536
1597
  time_in_force: TimeInForce = TimeInForce.DAY,
1537
1598
  price_round_method: Optional[TickRoundMethod] = None,
@@ -1551,7 +1612,7 @@ class AsyncClient:
1551
1612
  symbol: the symbol to send the order for
1552
1613
  execution_venue: the execution venue to send the order to,
1553
1614
  if execution_venue is set to None, the OMS will send the order to the primary_exchange
1554
- the primary_exchange can be deduced from `get_product_info`
1615
+ the primary_exchange can be deduced from `get_product_info` (generally will be "CME" or "US-EQUITIES")
1555
1616
  dir: the direction of the order, BUY or SELL
1556
1617
  quantity: the quantity of the order
1557
1618
  limit_price: the limit price of the order
@@ -1563,7 +1624,7 @@ class AsyncClient:
1563
1624
  While technically optional, for most order types, the account is required
1564
1625
  trader: the trader to send the order for, defaults to the user's trader
1565
1626
  for when sending order for another user, not relevant for vast majority of users
1566
- post_only: whether the order should be post only, not supported by all exchanges
1627
+ post_only: whether the order should be post only, NOT SUPPORTED BY ALL EXCHANGES (e.g. CME)
1567
1628
  trigger_price: the trigger price for the order, only relevant for stop / take_profit orders
1568
1629
  stop_loss_price: the stop loss price for a bracket order.
1569
1630
  profit_price: the take profit price for a bracket order.
@@ -1573,17 +1634,10 @@ class AsyncClient:
1573
1634
 
1574
1635
  If the order is rejected, the order.reject_reason and order.reject_message will be set
1575
1636
  """
1576
- grpc_client = await self.core()
1637
+ grpc_client = await self._core()
1577
1638
  assert quantity > 0, "quantity must be positive"
1578
1639
 
1579
- if dir is None:
1580
- if "odir" in kwargs and isinstance(kwargs["odir"], OrderDir):
1581
- logging.warning("odir is deprecated, use dir instead")
1582
- dir = kwargs["odir"]
1583
- else:
1584
- raise ValueError("dir is required")
1585
-
1586
- if price_round_method is not None:
1640
+ if limit_price is not None and price_round_method is not None:
1587
1641
  if execution_venue is None:
1588
1642
  product_info = await self.get_product_info(symbol)
1589
1643
  if product_info is None:
@@ -1727,7 +1781,7 @@ class AsyncClient:
1727
1781
  Returns:
1728
1782
  the CancelFields object
1729
1783
  """
1730
- grpc_client = await self.core()
1784
+ grpc_client = await self._core()
1731
1785
  req = CancelOrderRequest(id=order_id)
1732
1786
  res = await grpc_client.unary_unary(req)
1733
1787
  return res
@@ -1770,7 +1824,7 @@ class AsyncClient:
1770
1824
  # res = await grpc_client.unary_unary(req)
1771
1825
  # return True
1772
1826
 
1773
- async def create_algo_order(
1827
+ async def place_algo_order(
1774
1828
  self,
1775
1829
  *,
1776
1830
  params: SpreaderParams,
@@ -1780,7 +1834,7 @@ class AsyncClient:
1780
1834
  """
1781
1835
  Sends an advanced algo order such as the spreader.
1782
1836
  """
1783
- grpc_client = await self.core()
1837
+ grpc_client = await self._core()
1784
1838
 
1785
1839
  if isinstance(params, SpreaderParams):
1786
1840
  algo = "SPREADER"