architect-py 3.2.2__py3-none-any.whl → 5.0.0b2__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 (205) hide show
  1. architect_py/__init__.py +15 -2
  2. architect_py/async_client.py +1060 -643
  3. architect_py/client.py +25 -26
  4. architect_py/client_interface.py +63 -0
  5. architect_py/common_types/__init__.py +6 -0
  6. architect_py/common_types/order_dir.py +91 -0
  7. architect_py/common_types/scalars.py +25 -0
  8. architect_py/common_types/tradable_product.py +59 -0
  9. architect_py/graphql_client/__init__.py +2 -0
  10. architect_py/graphql_client/client.py +3 -6
  11. architect_py/graphql_client/enums.py +5 -0
  12. architect_py/graphql_client/fragments.py +3 -6
  13. architect_py/graphql_client/get_fills_query.py +2 -1
  14. architect_py/graphql_client/search_symbols_query.py +2 -1
  15. architect_py/graphql_client/subscribe_orderflow.py +2 -1
  16. architect_py/graphql_client/subscribe_trades.py +2 -1
  17. architect_py/grpc/__init__.py +143 -0
  18. architect_py/grpc/client.py +94 -0
  19. architect_py/{grpc_client → grpc/models}/Accounts/AccountsRequest.py +6 -3
  20. architect_py/{grpc_client → grpc/models}/Accounts/AccountsResponse.py +1 -1
  21. architect_py/{grpc_client → grpc/models}/Accounts/__init__.py +1 -1
  22. architect_py/grpc/models/Algo/AlgoOrder.py +114 -0
  23. architect_py/grpc/models/Algo/AlgoOrderRequest.py +46 -0
  24. architect_py/grpc/models/Algo/AlgoOrdersRequest.py +72 -0
  25. architect_py/grpc/models/Algo/AlgoOrdersResponse.py +27 -0
  26. architect_py/grpc/models/Algo/CreateAlgoOrderRequest.py +56 -0
  27. architect_py/grpc/models/Algo/PauseAlgoRequest.py +42 -0
  28. architect_py/grpc/models/Algo/PauseAlgoResponse.py +20 -0
  29. architect_py/grpc/models/Algo/StartAlgoRequest.py +42 -0
  30. architect_py/grpc/models/Algo/StartAlgoResponse.py +20 -0
  31. architect_py/grpc/models/Algo/StopAlgoRequest.py +42 -0
  32. architect_py/grpc/models/Algo/StopAlgoResponse.py +20 -0
  33. architect_py/{grpc_client → grpc/models}/Algo/__init__.py +1 -1
  34. architect_py/grpc/models/Auth/CreateJwtRequest.py +47 -0
  35. architect_py/grpc/models/Auth/CreateJwtResponse.py +23 -0
  36. architect_py/{grpc_client/Cpty → grpc/models/Auth}/__init__.py +1 -1
  37. architect_py/grpc/models/Boss/DepositsRequest.py +40 -0
  38. architect_py/grpc/models/Boss/DepositsResponse.py +27 -0
  39. architect_py/grpc/models/Boss/RqdAccountStatisticsRequest.py +42 -0
  40. architect_py/grpc/models/Boss/RqdAccountStatisticsResponse.py +25 -0
  41. architect_py/grpc/models/Boss/StatementUrlRequest.py +40 -0
  42. architect_py/grpc/models/Boss/StatementUrlResponse.py +23 -0
  43. architect_py/grpc/models/Boss/StatementsRequest.py +40 -0
  44. architect_py/grpc/models/Boss/StatementsResponse.py +27 -0
  45. architect_py/grpc/models/Boss/WithdrawalsRequest.py +40 -0
  46. architect_py/grpc/models/Boss/WithdrawalsResponse.py +27 -0
  47. architect_py/{grpc_client/Folio → grpc/models/Boss}/__init__.py +1 -1
  48. architect_py/grpc/models/Core/ConfigRequest.py +37 -0
  49. architect_py/grpc/models/Core/ConfigResponse.py +25 -0
  50. architect_py/grpc/models/Core/__init__.py +2 -0
  51. architect_py/{grpc_client → grpc/models}/Cpty/CptyRequest.py +2 -2
  52. architect_py/{grpc_client → grpc/models}/Cpty/CptyResponse.py +3 -3
  53. architect_py/{grpc_client → grpc/models}/Cpty/CptyStatus.py +1 -1
  54. architect_py/{grpc_client → grpc/models}/Cpty/CptyStatusRequest.py +2 -2
  55. architect_py/{grpc_client → grpc/models}/Cpty/CptysRequest.py +2 -2
  56. architect_py/{grpc_client → grpc/models}/Cpty/CptysResponse.py +1 -1
  57. architect_py/grpc/models/Cpty/__init__.py +2 -0
  58. architect_py/{grpc_client → grpc/models}/Folio/AccountHistoryRequest.py +2 -2
  59. architect_py/{grpc_client → grpc/models}/Folio/AccountHistoryResponse.py +1 -1
  60. architect_py/{grpc_client → grpc/models}/Folio/AccountSummariesRequest.py +2 -2
  61. architect_py/{grpc_client → grpc/models}/Folio/AccountSummariesResponse.py +1 -1
  62. architect_py/{grpc_client → grpc/models}/Folio/AccountSummary.py +1 -1
  63. architect_py/{grpc_client → grpc/models}/Folio/AccountSummaryRequest.py +2 -2
  64. architect_py/{grpc_client → grpc/models}/Folio/HistoricalFillsRequest.py +7 -4
  65. architect_py/{grpc_client → grpc/models}/Folio/HistoricalFillsResponse.py +1 -1
  66. architect_py/{grpc_client → grpc/models}/Folio/HistoricalOrdersRequest.py +3 -3
  67. architect_py/{grpc_client → grpc/models}/Folio/HistoricalOrdersResponse.py +1 -1
  68. architect_py/grpc/models/Folio/__init__.py +2 -0
  69. architect_py/{grpc_client → grpc/models}/Health/HealthCheckRequest.py +2 -2
  70. architect_py/{grpc_client → grpc/models}/Health/HealthCheckResponse.py +1 -1
  71. architect_py/grpc/models/Health/__init__.py +2 -0
  72. architect_py/{grpc_client → grpc/models}/Marketdata/Candle.py +1 -1
  73. architect_py/{grpc_client → grpc/models}/Marketdata/HistoricalCandlesRequest.py +11 -8
  74. architect_py/{grpc_client → grpc/models}/Marketdata/HistoricalCandlesResponse.py +1 -1
  75. architect_py/{grpc_client → grpc/models}/Marketdata/L1BookSnapshot.py +52 -5
  76. architect_py/{grpc_client → grpc/models}/Marketdata/L1BookSnapshotRequest.py +8 -3
  77. architect_py/{grpc_client → grpc/models}/Marketdata/L1BookSnapshotsRequest.py +6 -3
  78. architect_py/{grpc_client → grpc/models}/Marketdata/L2BookSnapshot.py +1 -1
  79. architect_py/{grpc_client → grpc/models}/Marketdata/L2BookSnapshotRequest.py +2 -2
  80. architect_py/{grpc_client → grpc/models}/Marketdata/Liquidation.py +2 -2
  81. architect_py/{grpc_client → grpc/models}/Marketdata/MarketStatus.py +1 -1
  82. architect_py/{grpc_client → grpc/models}/Marketdata/MarketStatusRequest.py +2 -2
  83. architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeCandlesRequest.py +2 -2
  84. architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeCurrentCandlesRequest.py +3 -4
  85. architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeL1BookSnapshotsRequest.py +6 -3
  86. architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeL2BookUpdatesRequest.py +2 -2
  87. architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeLiquidationsRequest.py +2 -2
  88. architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeManyCandlesRequest.py +2 -2
  89. architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeTickersRequest.py +2 -2
  90. architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeTradesRequest.py +2 -2
  91. architect_py/{grpc_client → grpc/models}/Marketdata/Ticker.py +1 -1
  92. architect_py/{grpc_client → grpc/models}/Marketdata/TickerRequest.py +2 -2
  93. architect_py/{grpc_client → grpc/models}/Marketdata/TickersRequest.py +2 -2
  94. architect_py/{grpc_client → grpc/models}/Marketdata/TickersResponse.py +1 -1
  95. architect_py/{grpc_client → grpc/models}/Marketdata/Trade.py +2 -2
  96. architect_py/grpc/models/Marketdata/__init__.py +2 -0
  97. architect_py/grpc/models/Oms/Cancel.py +90 -0
  98. architect_py/{grpc_client → grpc/models}/Oms/CancelAllOrdersRequest.py +2 -2
  99. architect_py/{grpc_client → grpc/models}/Oms/CancelAllOrdersResponse.py +1 -1
  100. architect_py/{grpc_client → grpc/models}/Oms/CancelOrderRequest.py +2 -2
  101. architect_py/{grpc_client → grpc/models}/Oms/OpenOrdersRequest.py +2 -2
  102. architect_py/{grpc_client → grpc/models}/Oms/OpenOrdersResponse.py +1 -1
  103. architect_py/{grpc_client → grpc/models}/Oms/Order.py +6 -13
  104. architect_py/{grpc_client → grpc/models}/Oms/PendingCancelsRequest.py +2 -2
  105. architect_py/{grpc_client → grpc/models}/Oms/PendingCancelsResponse.py +1 -1
  106. architect_py/{grpc_client → grpc/models}/Oms/PlaceOrderRequest.py +16 -23
  107. architect_py/grpc/models/Oms/__init__.py +2 -0
  108. architect_py/grpc/models/OptionsMarketdata/OptionsChain.py +30 -0
  109. architect_py/grpc/models/OptionsMarketdata/OptionsChainGreeks.py +30 -0
  110. architect_py/grpc/models/OptionsMarketdata/OptionsChainGreeksRequest.py +47 -0
  111. architect_py/grpc/models/OptionsMarketdata/OptionsChainRequest.py +45 -0
  112. architect_py/grpc/models/OptionsMarketdata/OptionsExpirations.py +29 -0
  113. architect_py/grpc/models/OptionsMarketdata/OptionsExpirationsRequest.py +42 -0
  114. architect_py/grpc/models/OptionsMarketdata/__init__.py +2 -0
  115. architect_py/{grpc_client → grpc/models}/Orderflow/DropcopyRequest.py +2 -2
  116. architect_py/{grpc_client → grpc/models}/Orderflow/OrderflowRequest.py +1 -1
  117. architect_py/{grpc_client → grpc/models}/Orderflow/SubscribeOrderflowRequest.py +2 -2
  118. architect_py/grpc/models/Orderflow/__init__.py +2 -0
  119. architect_py/grpc/models/Symbology/DownloadProductCatalogRequest.py +42 -0
  120. architect_py/grpc/models/Symbology/DownloadProductCatalogResponse.py +27 -0
  121. architect_py/grpc/models/Symbology/ExecutionInfoRequest.py +47 -0
  122. architect_py/grpc/models/Symbology/ExecutionInfoResponse.py +27 -0
  123. architect_py/{grpc_client → grpc/models}/Symbology/PruneExpiredSymbolsRequest.py +2 -2
  124. architect_py/{grpc_client → grpc/models}/Symbology/PruneExpiredSymbolsResponse.py +1 -1
  125. architect_py/{grpc_client → grpc/models}/Symbology/SubscribeSymbology.py +1 -1
  126. architect_py/{grpc_client → grpc/models}/Symbology/SymbologyRequest.py +2 -2
  127. architect_py/{grpc_client → grpc/models}/Symbology/SymbologySnapshot.py +7 -2
  128. architect_py/{grpc_client → grpc/models}/Symbology/SymbologyUpdate.py +9 -2
  129. architect_py/{grpc_client → grpc/models}/Symbology/SymbolsRequest.py +2 -2
  130. architect_py/{grpc_client → grpc/models}/Symbology/SymbolsResponse.py +1 -1
  131. architect_py/grpc/models/Symbology/UploadProductCatalogRequest.py +49 -0
  132. architect_py/grpc/models/Symbology/UploadProductCatalogResponse.py +20 -0
  133. architect_py/{grpc_client → grpc/models}/Symbology/UploadSymbologyRequest.py +2 -2
  134. architect_py/{grpc_client → grpc/models}/Symbology/UploadSymbologyResponse.py +1 -1
  135. architect_py/grpc/models/Symbology/__init__.py +2 -0
  136. architect_py/grpc/models/__init__.py +2 -0
  137. architect_py/{grpc_client → grpc/models}/definitions.py +690 -841
  138. architect_py/grpc/resolve_endpoint.py +70 -0
  139. architect_py/{grpc_client/grpc_server.py → grpc/server.py} +9 -6
  140. architect_py/grpc/utils.py +32 -0
  141. architect_py/internal_utils/__init__.py +0 -0
  142. architect_py/internal_utils/no_pandas.py +3 -0
  143. architect_py/tests/conftest.py +91 -87
  144. architect_py/tests/test_book_building.py +49 -50
  145. architect_py/tests/test_marketdata.py +168 -0
  146. architect_py/tests/test_order_entry.py +37 -0
  147. architect_py/tests/test_orderflow.py +41 -0
  148. architect_py/tests/test_portfolio_management.py +23 -0
  149. architect_py/tests/test_rounding.py +28 -28
  150. architect_py/tests/test_symbology.py +37 -30
  151. architect_py/utils/nearest_tick.py +2 -5
  152. architect_py/utils/nearest_tick_2.py +1 -2
  153. architect_py/utils/orderbook.py +35 -0
  154. architect_py/utils/pandas.py +44 -0
  155. architect_py/utils/price_bands.py +0 -3
  156. architect_py/utils/symbol_parsing.py +29 -0
  157. architect_py-5.0.0b2.dist-info/METADATA +123 -0
  158. architect_py-5.0.0b2.dist-info/RECORD +214 -0
  159. {architect_py-3.2.2.dist-info → architect_py-5.0.0b2.dist-info}/WHEEL +2 -1
  160. architect_py-5.0.0b2.dist-info/top_level.txt +4 -0
  161. examples/__init__.py +0 -0
  162. examples/book_subscription.py +52 -0
  163. examples/candles.py +30 -0
  164. examples/common.py +116 -0
  165. examples/external_cpty.py +77 -0
  166. examples/funding_rate_mean_reversion_algo.py +186 -0
  167. examples/order_sending.py +91 -0
  168. examples/stream_l1_marketdata.py +25 -0
  169. examples/stream_l2_marketdata.py +38 -0
  170. examples/trades.py +21 -0
  171. examples/tutorial_async.py +86 -0
  172. examples/tutorial_sync.py +95 -0
  173. scripts/generate_functions_md.py +166 -0
  174. scripts/generate_sync_interface.py +226 -0
  175. scripts/postprocess_grpc.py +604 -0
  176. scripts/preprocess_grpc_schema.py +708 -0
  177. templates/exceptions.py +83 -0
  178. templates/juniper_base_client.py +371 -0
  179. architect_py/client_protocol.py +0 -53
  180. architect_py/grpc_client/Algo/AlgoOrderForTwapAlgo.py +0 -61
  181. architect_py/grpc_client/Algo/CreateAlgoOrderRequestForTwapAlgo.py +0 -59
  182. architect_py/grpc_client/Algo/ModifyAlgoOrderRequestForTwapAlgo.py +0 -45
  183. architect_py/grpc_client/Health/__init__.py +0 -2
  184. architect_py/grpc_client/Marketdata/__init__.py +0 -2
  185. architect_py/grpc_client/Oms/Cancel.py +0 -42
  186. architect_py/grpc_client/Oms/__init__.py +0 -2
  187. architect_py/grpc_client/Orderflow/__init__.py +0 -2
  188. architect_py/grpc_client/Symbology/__init__.py +0 -2
  189. architect_py/grpc_client/__init__.py +0 -2
  190. architect_py/grpc_client/grpc_client.py +0 -413
  191. architect_py/scalars.py +0 -172
  192. architect_py/tests/test_accounts.py +0 -31
  193. architect_py/tests/test_client.py +0 -29
  194. architect_py/tests/test_grpc_client.py +0 -30
  195. architect_py/tests/test_order_sending.py +0 -65
  196. architect_py/tests/test_snapshots.py +0 -52
  197. architect_py/tests/test_subscriptions.py +0 -126
  198. architect_py-3.2.2.dist-info/METADATA +0 -191
  199. architect_py-3.2.2.dist-info/RECORD +0 -148
  200. /architect_py/{grpc_client → grpc/models}/Marketdata/ArrayOfL1BookSnapshot.py +0 -0
  201. /architect_py/{grpc_client → grpc/models}/Marketdata/L2BookUpdate.py +0 -0
  202. /architect_py/{grpc_client → grpc/models}/Marketdata/TickerUpdate.py +0 -0
  203. /architect_py/{grpc_client → grpc/models}/Orderflow/Dropcopy.py +0 -0
  204. /architect_py/{grpc_client → grpc/models}/Orderflow/Orderflow.py +0 -0
  205. {architect_py-3.2.2.dist-info → architect_py-5.0.0b2.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,83 @@
1
+ from typing import Any, Dict, List, Optional, Union
2
+
3
+ import httpx
4
+
5
+
6
+ class GraphQLClientError(Exception):
7
+ """Base exception."""
8
+
9
+
10
+ class GraphQLClientHttpError(GraphQLClientError):
11
+ def __init__(self, status_code: int, response: httpx.Response) -> None:
12
+ self.status_code = status_code
13
+ self.response = response
14
+
15
+ def __str__(self) -> str:
16
+ return f"HTTP status code: {self.status_code}"
17
+
18
+
19
+ class GraphQLClientInvalidResponseError(GraphQLClientError):
20
+ def __init__(self, response: httpx.Response) -> None:
21
+ self.response = response
22
+
23
+ def __str__(self) -> str:
24
+ return "Invalid response format."
25
+
26
+
27
+ class GraphQLClientGraphQLError(GraphQLClientError):
28
+ def __init__(
29
+ self,
30
+ message: str,
31
+ locations: Optional[List[Dict[str, int]]] = None,
32
+ path: Optional[List[str]] = None,
33
+ extensions: Optional[Dict[str, object]] = None,
34
+ orginal: Optional[Dict[str, object]] = None,
35
+ ):
36
+ self.message = message
37
+ self.locations = locations
38
+ self.path = path
39
+ self.extensions = extensions
40
+ self.orginal = orginal
41
+
42
+ def __str__(self) -> str:
43
+ return self.message
44
+
45
+ @classmethod
46
+ def from_dict(cls, error: Dict[str, Any]) -> "GraphQLClientGraphQLError":
47
+ return cls(
48
+ message=error["message"],
49
+ locations=error.get("locations"),
50
+ path=error.get("path"),
51
+ extensions=error.get("extensions"),
52
+ orginal=error,
53
+ )
54
+
55
+
56
+ class GraphQLClientGraphQLMultiError(GraphQLClientError):
57
+ def __init__(
58
+ self,
59
+ errors: List[GraphQLClientGraphQLError],
60
+ data: Optional[Dict[str, Any]] = None,
61
+ ):
62
+ self.errors = errors
63
+ self.data = data
64
+
65
+ def __str__(self) -> str:
66
+ return "; ".join(str(e) for e in self.errors)
67
+
68
+ @classmethod
69
+ def from_errors_dicts(
70
+ cls, errors_dicts: List[Dict[str, Any]], data: Optional[Dict[str, Any]] = None
71
+ ) -> "GraphQLClientGraphQLMultiError":
72
+ return cls(
73
+ errors=[GraphQLClientGraphQLError.from_dict(e) for e in errors_dicts],
74
+ data=data,
75
+ )
76
+
77
+
78
+ class GraphQLClientInvalidMessageFormat(GraphQLClientError):
79
+ def __init__(self, message: Union[str, bytes]) -> None:
80
+ self.message = message
81
+
82
+ def __str__(self) -> str:
83
+ return "Invalid message format."
@@ -0,0 +1,371 @@
1
+ """
2
+ Changes made from default base client:
3
+ - Corrected the `Self` type variable from the `JuniperBaseClient` class
4
+ - Removed the try-except block from importing websockets
5
+ - Removed the # Generated by ariadne-codegen at top
6
+ - Redid init to add auth + port
7
+ """
8
+
9
+ import enum
10
+ import json
11
+ from typing import Any, AsyncIterator, cast, Dict, IO, List, Optional, Tuple, TypeVar
12
+ from uuid import uuid4
13
+
14
+ import httpx
15
+ from pydantic import BaseModel
16
+ from pydantic_core import to_jsonable_python
17
+
18
+ from websockets.client import ( # type: ignore[import-not-found,unused-ignore]
19
+ connect as ws_connect,
20
+ WebSocketClientProtocol,
21
+ )
22
+ from websockets.typing import ( # type: ignore[import-not-found,unused-ignore]
23
+ Data,
24
+ Origin,
25
+ Subprotocol,
26
+ )
27
+
28
+ from architect_py.graphql_client.base_model import UNSET, Upload
29
+ from .exceptions import (
30
+ GraphQLClientGraphQLMultiError,
31
+ GraphQLClientHttpError,
32
+ GraphQLClientInvalidMessageFormat,
33
+ GraphQLClientInvalidResponseError,
34
+ )
35
+
36
+ Self = TypeVar("Self", bound="JuniperBaseClient")
37
+
38
+ GRAPHQL_TRANSPORT_WS = "graphql-transport-ws"
39
+
40
+
41
+ class GraphQLTransportWSMessageType(str, enum.Enum):
42
+ CONNECTION_INIT = "connection_init"
43
+ CONNECTION_ACK = "connection_ack"
44
+ PING = "ping"
45
+ PONG = "pong"
46
+ SUBSCRIBE = "subscribe"
47
+ NEXT = "next"
48
+ ERROR = "error"
49
+ COMPLETE = "complete"
50
+
51
+
52
+ class JuniperBaseClient:
53
+ def __init__(
54
+ self,
55
+ host: str = "",
56
+ port: int = 4567,
57
+ use_tls: bool = True,
58
+ api_key: Optional[str] = None,
59
+ api_secret: Optional[str] = None,
60
+ http_client: Optional[httpx.AsyncClient] = None,
61
+ ws_origin: Optional[str] = None,
62
+ ) -> None:
63
+ protocol = "https" if use_tls else "http"
64
+ self.url = f"{protocol}://{host}:{port}/graphql"
65
+ self.headers = {}
66
+ if api_key and api_secret:
67
+ self.headers["Authorization"] = f"Basic {api_key} {api_secret}"
68
+ self.http_client = (
69
+ http_client if http_client else httpx.AsyncClient(headers=self.headers)
70
+ )
71
+
72
+ ws_protocol = "wss" if use_tls else "ws"
73
+ self.ws_url = f"{ws_protocol}://{host}:{port}/subscriptions/graphql"
74
+ self.ws_headers: dict = {} # CR acho: I don't think this does anything
75
+ self.ws_origin = Origin(ws_origin) if ws_origin else None
76
+ self.ws_connection_init_payload: dict[str, str] = {}
77
+ if api_key and api_secret:
78
+ self.ws_connection_init_payload["authorization"] = (
79
+ f"Basic {api_key} {api_secret}"
80
+ )
81
+
82
+ async def __aenter__(self: Self) -> Self:
83
+ return self
84
+
85
+ async def __aexit__(
86
+ self,
87
+ exc_type: object,
88
+ exc_val: object,
89
+ exc_tb: object,
90
+ ) -> None:
91
+ await self.http_client.aclose()
92
+
93
+ async def execute(
94
+ self,
95
+ query: str,
96
+ operation_name: Optional[str] = None,
97
+ variables: Optional[Dict[str, Any]] = None,
98
+ **kwargs: Any,
99
+ ) -> httpx.Response:
100
+ processed_variables, files, files_map = self._process_variables(variables)
101
+
102
+ if files and files_map:
103
+ return await self._execute_multipart(
104
+ query=query,
105
+ operation_name=operation_name,
106
+ variables=processed_variables,
107
+ files=files,
108
+ files_map=files_map,
109
+ **kwargs,
110
+ )
111
+
112
+ return await self._execute_json(
113
+ query=query,
114
+ operation_name=operation_name,
115
+ variables=processed_variables,
116
+ **kwargs,
117
+ )
118
+
119
+ def get_data(self, response: httpx.Response) -> Dict[str, Any]:
120
+ if not response.is_success:
121
+ raise GraphQLClientHttpError(
122
+ status_code=response.status_code, response=response
123
+ )
124
+
125
+ try:
126
+ response_json = response.json()
127
+ except ValueError as exc:
128
+ raise GraphQLClientInvalidResponseError(response=response) from exc
129
+
130
+ if (not isinstance(response_json, dict)) or (
131
+ "data" not in response_json and "errors" not in response_json
132
+ ):
133
+ raise GraphQLClientInvalidResponseError(response=response)
134
+
135
+ data = response_json.get("data")
136
+ errors = response_json.get("errors")
137
+
138
+ if errors:
139
+ raise GraphQLClientGraphQLMultiError.from_errors_dicts(
140
+ errors_dicts=errors, data=data
141
+ )
142
+
143
+ return cast(Dict[str, Any], data)
144
+
145
+ async def execute_ws(
146
+ self,
147
+ query: str,
148
+ operation_name: Optional[str] = None,
149
+ variables: Optional[Dict[str, Any]] = None,
150
+ **kwargs: Any,
151
+ ) -> AsyncIterator[Dict[str, Any]]:
152
+ headers = self.ws_headers.copy()
153
+ headers.update(kwargs.get("extra_headers", {}))
154
+
155
+ merged_kwargs: Dict[str, Any] = {"origin": self.ws_origin}
156
+ merged_kwargs.update(kwargs)
157
+ merged_kwargs["extra_headers"] = headers
158
+ merged_kwargs["ping_interval"] = None
159
+
160
+ operation_id = str(uuid4())
161
+ async with ws_connect(
162
+ self.ws_url,
163
+ subprotocols=[Subprotocol(GRAPHQL_TRANSPORT_WS)],
164
+ **merged_kwargs,
165
+ ) as websocket:
166
+ await self._send_connection_init(websocket)
167
+ # wait for connection_ack from server
168
+ await self._handle_ws_message(
169
+ await websocket.recv(),
170
+ websocket,
171
+ expected_type=GraphQLTransportWSMessageType.CONNECTION_ACK,
172
+ )
173
+ await self._send_subscribe(
174
+ websocket,
175
+ operation_id=operation_id,
176
+ query=query,
177
+ operation_name=operation_name,
178
+ variables=variables,
179
+ )
180
+
181
+ async for message in websocket:
182
+ data = await self._handle_ws_message(message, websocket)
183
+ if data:
184
+ yield data
185
+
186
+ def _process_variables(
187
+ self, variables: Optional[Dict[str, Any]]
188
+ ) -> Tuple[
189
+ Dict[str, Any], Dict[str, Tuple[str, IO[bytes], str]], Dict[str, List[str]]
190
+ ]:
191
+ if not variables:
192
+ return {}, {}, {}
193
+
194
+ serializable_variables = self._convert_dict_to_json_serializable(variables)
195
+ return self._get_files_from_variables(serializable_variables)
196
+
197
+ def _convert_dict_to_json_serializable(
198
+ self, dict_: Dict[str, Any]
199
+ ) -> Dict[str, Any]:
200
+ return {
201
+ key: self._convert_value(value)
202
+ for key, value in dict_.items()
203
+ if value is not UNSET
204
+ }
205
+
206
+ def _convert_value(self, value: Any) -> Any:
207
+ if isinstance(value, BaseModel):
208
+ return value.model_dump(by_alias=True, exclude_unset=True)
209
+ if isinstance(value, list):
210
+ return [self._convert_value(item) for item in value]
211
+ return value
212
+
213
+ def _get_files_from_variables(
214
+ self, variables: Dict[str, Any]
215
+ ) -> Tuple[
216
+ Dict[str, Any], Dict[str, Tuple[str, IO[bytes], str]], Dict[str, List[str]]
217
+ ]:
218
+ files_map: Dict[str, List[str]] = {}
219
+ files_list: List[Upload] = []
220
+
221
+ def separate_files(path: str, obj: Any) -> Any:
222
+ if isinstance(obj, list):
223
+ nulled_list = []
224
+ for index, value in enumerate(obj):
225
+ value = separate_files(f"{path}.{index}", value)
226
+ nulled_list.append(value)
227
+ return nulled_list
228
+
229
+ if isinstance(obj, dict):
230
+ nulled_dict = {}
231
+ for key, value in obj.items():
232
+ value = separate_files(f"{path}.{key}", value)
233
+ nulled_dict[key] = value
234
+ return nulled_dict
235
+
236
+ if isinstance(obj, Upload):
237
+ if obj in files_list:
238
+ file_index = files_list.index(obj)
239
+ files_map[str(file_index)].append(path)
240
+ else:
241
+ file_index = len(files_list)
242
+ files_list.append(obj)
243
+ files_map[str(file_index)] = [path]
244
+ return None
245
+
246
+ return obj
247
+
248
+ nulled_variables = separate_files("variables", variables)
249
+ files: Dict[str, Tuple[str, IO[bytes], str]] = {
250
+ str(i): (file_.filename, cast(IO[bytes], file_.content), file_.content_type)
251
+ for i, file_ in enumerate(files_list)
252
+ }
253
+ return nulled_variables, files, files_map
254
+
255
+ async def _execute_multipart(
256
+ self,
257
+ query: str,
258
+ operation_name: Optional[str],
259
+ variables: Dict[str, Any],
260
+ files: Dict[str, Tuple[str, IO[bytes], str]],
261
+ files_map: Dict[str, List[str]],
262
+ **kwargs: Any,
263
+ ) -> httpx.Response:
264
+ data = {
265
+ "operations": json.dumps(
266
+ {
267
+ "query": query,
268
+ "operationName": operation_name,
269
+ "variables": variables,
270
+ },
271
+ default=to_jsonable_python,
272
+ ),
273
+ "map": json.dumps(files_map, default=to_jsonable_python),
274
+ }
275
+
276
+ return await self.http_client.post(
277
+ url=self.url, data=data, files=files, **kwargs
278
+ )
279
+
280
+ async def _execute_json(
281
+ self,
282
+ query: str,
283
+ operation_name: Optional[str],
284
+ variables: Dict[str, Any],
285
+ **kwargs: Any,
286
+ ) -> httpx.Response:
287
+ headers: Dict[str, str] = {"Content-Type": "application/json"}
288
+ headers.update(kwargs.get("headers", {}))
289
+
290
+ merged_kwargs: Dict[str, Any] = kwargs.copy()
291
+ merged_kwargs["headers"] = headers
292
+
293
+ return await self.http_client.post(
294
+ url=self.url,
295
+ content=json.dumps(
296
+ {
297
+ "query": query,
298
+ "operationName": operation_name,
299
+ "variables": variables,
300
+ },
301
+ default=to_jsonable_python,
302
+ ),
303
+ **merged_kwargs,
304
+ )
305
+
306
+ async def _send_connection_init(self, websocket: WebSocketClientProtocol) -> None:
307
+ payload: Dict[str, Any] = {
308
+ "type": GraphQLTransportWSMessageType.CONNECTION_INIT.value
309
+ }
310
+ if self.ws_connection_init_payload:
311
+ payload["payload"] = self.ws_connection_init_payload
312
+ await websocket.send(json.dumps(payload))
313
+
314
+ async def _send_subscribe(
315
+ self,
316
+ websocket: WebSocketClientProtocol,
317
+ operation_id: str,
318
+ query: str,
319
+ operation_name: Optional[str] = None,
320
+ variables: Optional[Dict[str, Any]] = None,
321
+ ) -> None:
322
+ payload: Dict[str, Any] = {
323
+ "id": operation_id,
324
+ "type": GraphQLTransportWSMessageType.SUBSCRIBE.value,
325
+ "payload": {"query": query, "operationName": operation_name},
326
+ }
327
+ if variables:
328
+ payload["payload"]["variables"] = self._convert_dict_to_json_serializable(
329
+ variables
330
+ )
331
+ await websocket.send(json.dumps(payload))
332
+
333
+ async def _handle_ws_message(
334
+ self,
335
+ message: Data,
336
+ websocket: WebSocketClientProtocol,
337
+ expected_type: Optional[GraphQLTransportWSMessageType] = None,
338
+ ) -> Optional[Dict[str, Any]]:
339
+ try:
340
+ message_dict = json.loads(message)
341
+ except json.JSONDecodeError as exc:
342
+ raise GraphQLClientInvalidMessageFormat(message=message) from exc
343
+
344
+ type_ = message_dict.get("type")
345
+ payload = message_dict.get("payload", {})
346
+
347
+ if not type_ or type_ not in {t.value for t in GraphQLTransportWSMessageType}:
348
+ raise GraphQLClientInvalidMessageFormat(message=message)
349
+
350
+ if expected_type and expected_type != type_:
351
+ raise GraphQLClientInvalidMessageFormat(
352
+ f"Invalid message received. Expected: {expected_type.value}"
353
+ )
354
+
355
+ if type_ == GraphQLTransportWSMessageType.NEXT:
356
+ if "data" not in payload:
357
+ raise GraphQLClientInvalidMessageFormat(message=message)
358
+ return cast(Dict[str, Any], payload["data"])
359
+
360
+ if type_ == GraphQLTransportWSMessageType.COMPLETE:
361
+ await websocket.close()
362
+ elif type_ == GraphQLTransportWSMessageType.PING:
363
+ await websocket.send(
364
+ json.dumps({"type": GraphQLTransportWSMessageType.PONG.value})
365
+ )
366
+ elif type_ == GraphQLTransportWSMessageType.ERROR:
367
+ raise GraphQLClientGraphQLMultiError.from_errors_dicts(
368
+ errors_dicts=payload, data=message_dict
369
+ )
370
+
371
+ return None
@@ -1,53 +0,0 @@
1
- # fmt: off
2
-
3
- # mypy: ignore-errors
4
-
5
- # Autogenerated from generate_protocol.py
6
-
7
- # If you are here for function definitions, please refer to architect_py/async_cline.py
8
- # This file is so that the sync client has good type hinting
9
- # It is not used for anything else
10
- # For maintainers: ensure that the types in this file are correct for correct type hinting
11
-
12
-
13
- from architect_py.graphql_client import *
14
- from architect_py.async_client import *
15
-
16
-
17
- class AsyncClientProtocol:
18
- def cancel_all_orders(self) -> bool: ...
19
- def cancel_order(self, order_id: str) -> CancelFields: ...
20
- @staticmethod
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: ...
23
- def get_account_history(self, account: str, from_inclusive: Optional[datetime] = None, to_exclusive: Optional[datetime] = None) -> Sequence[AccountSummaryFields]: ...
24
- def get_account_summaries(self, accounts: Optional[list[str]] = None, trader: Optional[str] = None) -> Sequence[AccountSummaryFields]: ...
25
- def get_account_summary(self, account: str) -> AccountSummaryFields: ...
26
- def get_all_open_orders(self) -> Sequence[OrderFields]: ...
27
- def get_cme_first_notice_date(self, symbol: str) -> Optional[date]: ...
28
- def get_cme_future_from_root_month_year(self, root: str, month: int, year: int) -> str: ...
29
- def get_cme_futures_series(self, series: str) -> list[tuple[date, str]]: ...
30
- def get_execution_info(self, symbol: TradableProduct, execution_venue: str) -> Optional[ExecutionInfoFields]: ...
31
- def get_execution_infos(self, symbols: Optional[list[TradableProduct]], execution_venue: Optional[str] = None) -> Sequence[ExecutionInfoFields]: ...
32
- @staticmethod
33
- def get_expiration_from_CME_name(name: str) -> date: ...
34
- def get_fills(self, from_inclusive: Optional[datetime], to_exclusive: Optional[datetime], venue: Optional[str] = None, account: Optional[str] = None, order_id: Optional[str] = None) -> GetFillsQueryFolioHistoricalFills: ...
35
- def get_future_series(self, series_symbol: str) -> list[str]: ...
36
- def get_historical_candles(self, symbol: str, candle_width: CandleWidth, start: datetime, end: datetime) -> HistoricalCandlesResponse: ...
37
- def get_historical_orders(self, order_ids: Optional[list[str]] = None, from_inclusive: Optional[datetime] = None, to_exclusive: Optional[datetime] = None, venue: Optional[str] = None, account: Optional[str] = None, parent_order_id: Optional[str] = None) -> Sequence[OrderFields]: ...
38
- def get_l1_book_snapshot(self, symbol: str, venue: str) -> MarketTickerFields: ...
39
- def get_l1_book_snapshots(self, symbols: list[str], venue: str) -> Sequence[MarketTickerFields]: ...
40
- def get_l2_book_snapshot(self, symbol: str, venue: str) -> L2BookFields: ...
41
- def get_market_snapshot(self, symbol: TradableProduct, venue: str) -> MarketTickerFields: ...
42
- def get_market_snapshots(self, symbols: list[TradableProduct], venue: str) -> Sequence[MarketTickerFields]: ...
43
- def get_market_status(self, symbol: TradableProduct, venue: str) -> MarketStatusFields: ...
44
- def get_open_orders(self, order_ids: Optional[list[str]] = None, venue: Optional[str] = None, account: Optional[str] = None, trader: Optional[str] = None, symbol: Optional[str] = None, parent_order_id: Optional[str] = None) -> Sequence[OrderFields]: ...
45
- def get_order(self, order_id: str) -> Optional[OrderFields]: ...
46
- def get_orders(self, order_ids: list[str]) -> list[Optional[OrderFields]]: ...
47
- def get_product_info(self, symbol: str) -> Optional[ProductInfoFields]: ...
48
- def get_product_infos(self, symbols: Optional[list[str]]) -> Sequence[ProductInfoFields]: ...
49
- def list_accounts(self) -> Sequence[AccountWithPermissionsFields]: ...
50
- def search_symbols(self, search_string: Optional[str] = None, execution_venue: Optional[str] = None, offset: int = 0, limit: int = 20) -> list[TradableProduct]: ...
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: ...
53
- def who_am_i(self) -> tuple[str, str]: ...
@@ -1,61 +0,0 @@
1
- # generated by datamodel-codegen:
2
- # filename: Algo/AlgoOrderForTwapAlgo.json
3
-
4
- from __future__ import annotations
5
-
6
- from datetime import datetime
7
- from typing import List, Optional
8
-
9
- from msgspec import Struct
10
-
11
- from .. import definitions
12
-
13
-
14
- class AlgoOrderForTwapAlgo(Struct, omit_defaults=True):
15
- account: str
16
- algo_name: str
17
- algo_order_id: definitions.OrderId
18
- create_time: datetime
19
- params: definitions.TwapParams
20
- state: definitions.AlgoState
21
- status: definitions.TwapStatus
22
- trader: definitions.UserId
23
- display_symbols: Optional[List[str]] = None
24
- last_error: Optional[str] = None
25
- last_error_time: Optional[datetime] = None
26
- parent_order_id: Optional[definitions.OrderId] = None
27
-
28
- # below is a constructor that takes all field titles as arguments for convenience
29
- @classmethod
30
- def new(
31
- cls,
32
- account: str,
33
- algo_name: str,
34
- algo_order_id: definitions.OrderId,
35
- create_time: datetime,
36
- params: definitions.TwapParams,
37
- state: definitions.AlgoState,
38
- status: definitions.TwapStatus,
39
- trader: definitions.UserId,
40
- display_symbols: Optional[List[str]] = None,
41
- last_error: Optional[str] = None,
42
- last_error_time: Optional[datetime] = None,
43
- parent_order_id: Optional[definitions.OrderId] = None,
44
- ):
45
- return cls(
46
- account,
47
- algo_name,
48
- algo_order_id,
49
- create_time,
50
- params,
51
- state,
52
- status,
53
- trader,
54
- display_symbols,
55
- last_error,
56
- last_error_time,
57
- parent_order_id,
58
- )
59
-
60
- def __str__(self) -> str:
61
- return f"AlgoOrderForTwapAlgo(account={self.account},algo_name={self.algo_name},algo_order_id={self.algo_order_id},create_time={self.create_time},params={self.params},state={self.state},status={self.status},trader={self.trader},display_symbols={self.display_symbols},last_error={self.last_error},last_error_time={self.last_error_time},parent_order_id={self.parent_order_id})"
@@ -1,59 +0,0 @@
1
- # generated by datamodel-codegen:
2
- # filename: Algo/CreateAlgoOrderRequestForTwapAlgo.json
3
-
4
- from __future__ import annotations
5
- from architect_py.grpc_client.Algo.AlgoOrderForTwapAlgo import AlgoOrderForTwapAlgo
6
-
7
- from typing import Optional
8
-
9
- from msgspec import Struct
10
-
11
- from .. import definitions
12
-
13
-
14
- class CreateAlgoOrderRequestForTwapAlgo(Struct, omit_defaults=True):
15
- algo_name: str
16
- params: definitions.TwapParams
17
- account: Optional[str] = None
18
- algo_order_id: Optional[definitions.OrderId] = None
19
- parent_order_id: Optional[definitions.OrderId] = None
20
- trader: Optional[definitions.UserId] = None
21
-
22
- # below is a constructor that takes all field titles as arguments for convenience
23
- @classmethod
24
- def new(
25
- cls,
26
- algo_name: str,
27
- params: definitions.TwapParams,
28
- account: Optional[str] = None,
29
- algo_order_id: Optional[definitions.OrderId] = None,
30
- parent_order_id: Optional[definitions.OrderId] = None,
31
- trader: Optional[definitions.UserId] = None,
32
- ):
33
- return cls(
34
- algo_name,
35
- params,
36
- account,
37
- algo_order_id,
38
- parent_order_id,
39
- trader,
40
- )
41
-
42
- def __str__(self) -> str:
43
- return f"CreateAlgoOrderRequestForTwapAlgo(algo_name={self.algo_name},params={self.params},account={self.account},algo_order_id={self.algo_order_id},parent_order_id={self.parent_order_id},trader={self.trader})"
44
-
45
- @staticmethod
46
- def get_response_type():
47
- return AlgoOrderForTwapAlgo
48
-
49
- @staticmethod
50
- def get_unannotated_response_type():
51
- return AlgoOrderForTwapAlgo
52
-
53
- @staticmethod
54
- def get_route() -> str:
55
- return "/json.architect.Algo/CreateTwapAlgoOrder"
56
-
57
- @staticmethod
58
- def get_rpc_method():
59
- return "unary"
@@ -1,45 +0,0 @@
1
- # generated by datamodel-codegen:
2
- # filename: Algo/ModifyAlgoOrderRequestForTwapAlgo.json
3
-
4
- from __future__ import annotations
5
- from architect_py.grpc_client.Algo.AlgoOrderForTwapAlgo import AlgoOrderForTwapAlgo
6
-
7
- from msgspec import Struct
8
-
9
- from .. import definitions
10
-
11
-
12
- class ModifyAlgoOrderRequestForTwapAlgo(Struct, omit_defaults=True):
13
- algo_order_id: definitions.OrderId
14
- params: definitions.TwapParams
15
-
16
- # below is a constructor that takes all field titles as arguments for convenience
17
- @classmethod
18
- def new(
19
- cls,
20
- algo_order_id: definitions.OrderId,
21
- params: definitions.TwapParams,
22
- ):
23
- return cls(
24
- algo_order_id,
25
- params,
26
- )
27
-
28
- def __str__(self) -> str:
29
- return f"ModifyAlgoOrderRequestForTwapAlgo(algo_order_id={self.algo_order_id},params={self.params})"
30
-
31
- @staticmethod
32
- def get_response_type():
33
- return AlgoOrderForTwapAlgo
34
-
35
- @staticmethod
36
- def get_unannotated_response_type():
37
- return AlgoOrderForTwapAlgo
38
-
39
- @staticmethod
40
- def get_route() -> str:
41
- return "/json.architect.Algo/ModifyTwapAlgoOrder"
42
-
43
- @staticmethod
44
- def get_rpc_method():
45
- return "unary"
@@ -1,2 +0,0 @@
1
- # generated by datamodel-codegen:
2
- # filename: processed_schemas
@@ -1,2 +0,0 @@
1
- # generated by datamodel-codegen:
2
- # filename: processed_schemas