architect-py 3.2.1__py3-none-any.whl → 5.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. architect_py/__init__.py +18 -2
  2. architect_py/async_client.py +1089 -658
  3. architect_py/client.py +36 -33
  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 +145 -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 +5 -4
  52. architect_py/{grpc_client → grpc/models}/Cpty/CptyResponse.py +6 -6
  53. architect_py/grpc/models/Cpty/CptyStatus.py +48 -0
  54. architect_py/grpc/models/Cpty/CptyStatusRequest.py +45 -0
  55. architect_py/grpc/models/Cpty/CptysRequest.py +37 -0
  56. architect_py/grpc/models/Cpty/CptysResponse.py +27 -0
  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 +14 -3
  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 +2 -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 +671 -934
  138. architect_py/grpc/resolve_endpoint.py +70 -0
  139. architect_py/{grpc_client/grpc_server.py → grpc/server.py} +13 -9
  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 -85
  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.0.dist-info/METADATA +54 -0
  158. architect_py-5.0.0.dist-info/RECORD +214 -0
  159. {architect_py-3.2.1.dist-info → architect_py-5.0.0.dist-info}/WHEEL +2 -1
  160. architect_py-5.0.0.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 -52
  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/Folio/AggregatedAccountSummariesRequest.py +0 -59
  184. architect_py/grpc_client/Folio/AggregatedAccountSummariesResponse.py +0 -27
  185. architect_py/grpc_client/Health/__init__.py +0 -2
  186. architect_py/grpc_client/Marketdata/__init__.py +0 -2
  187. architect_py/grpc_client/Oms/Cancel.py +0 -42
  188. architect_py/grpc_client/Oms/__init__.py +0 -2
  189. architect_py/grpc_client/Orderflow/__init__.py +0 -2
  190. architect_py/grpc_client/Symbology/__init__.py +0 -2
  191. architect_py/grpc_client/__init__.py +0 -2
  192. architect_py/grpc_client/grpc_client.py +0 -405
  193. architect_py/scalars.py +0 -172
  194. architect_py/tests/test_accounts.py +0 -31
  195. architect_py/tests/test_client.py +0 -29
  196. architect_py/tests/test_grpc_client.py +0 -30
  197. architect_py/tests/test_order_sending.py +0 -61
  198. architect_py/tests/test_snapshots.py +0 -52
  199. architect_py/tests/test_subscriptions.py +0 -129
  200. architect_py-3.2.1.dist-info/METADATA +0 -212
  201. architect_py-3.2.1.dist-info/RECORD +0 -146
  202. /architect_py/{grpc_client → grpc/models}/Marketdata/ArrayOfL1BookSnapshot.py +0 -0
  203. /architect_py/{grpc_client → grpc/models}/Marketdata/L2BookUpdate.py +0 -0
  204. /architect_py/{grpc_client → grpc/models}/Marketdata/TickerUpdate.py +0 -0
  205. /architect_py/{grpc_client → grpc/models}/Orderflow/Dropcopy.py +0 -0
  206. /architect_py/{grpc_client → grpc/models}/Orderflow/Orderflow.py +0 -0
  207. {architect_py-3.2.1.dist-info → architect_py-5.0.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,41 @@
1
+ """
2
+ import asyncio
3
+
4
+ import pytest
5
+
6
+ from architect_py.async_client import AsyncClient
7
+ from architect_py.grpc.models.Orderflow.OrderflowRequest import OrderflowRequest
8
+
9
+
10
+ class OrderflowAsyncIterator:
11
+ queue: list[OrderflowRequest]
12
+ condition: asyncio.Condition
13
+
14
+ def __init__(self):
15
+ self.queue: list[OrderflowRequest] = []
16
+ self.condition: asyncio.Condition = asyncio.Condition()
17
+
18
+ def __aiter__(self):
19
+ return self
20
+
21
+ async def __anext__(self) -> OrderflowRequest:
22
+ async with self.condition:
23
+ while not self.queue:
24
+ await self.condition.wait()
25
+ return self.queue.pop(0)
26
+
27
+ async def add_to_queue(self, item: OrderflowRequest):
28
+ async with self.condition:
29
+ self.queue.append(item)
30
+ self.condition.notify()
31
+
32
+
33
+ @pytest.mark.asyncio
34
+ @pytest.mark.timeout(10)
35
+ async def test_orderflow(async_client: AsyncClient):
36
+ oai = OrderflowAsyncIterator()
37
+ async for of in async_client.orderflow(oai):
38
+ assert of is not None
39
+ return
40
+
41
+ """
@@ -0,0 +1,23 @@
1
+ import pytest
2
+
3
+ from architect_py import AsyncClient
4
+
5
+
6
+ @pytest.mark.asyncio
7
+ async def test_list_accounts(async_client: AsyncClient):
8
+ accounts = await async_client.list_accounts()
9
+ assert accounts is not None
10
+ assert len(accounts) > 0
11
+
12
+
13
+ @pytest.mark.asyncio
14
+ async def test_get_account_summary(async_client: AsyncClient):
15
+ accounts = await async_client.list_accounts()
16
+ assert accounts is not None
17
+ assert len(accounts) > 0
18
+
19
+ summary = await async_client.get_account_summary(account=accounts[0].account.name)
20
+ assert summary is not None
21
+ assert summary.balances is not None
22
+ assert len(summary.balances) > 0
23
+ assert summary.positions is not None
@@ -1,41 +1,41 @@
1
- from decimal import Decimal
1
+ # from decimal import Decimal
2
2
 
3
- from architect_py.utils.nearest_tick import TickRoundMethod
3
+ # from architect_py.utils.nearest_tick import TickRoundMethod
4
4
 
5
5
 
6
- def test_rounding():
7
- # Example usage
8
- value = Decimal("123.454")
9
- tick_size = Decimal("0.01")
6
+ # def test_rounding():
7
+ # # Example usage
8
+ # value = Decimal("123.454")
9
+ # tick_size = Decimal("0.01")
10
10
 
11
- rounded_value = TickRoundMethod.ROUND(value, tick_size=tick_size)
12
- assert rounded_value == Decimal("123.45")
11
+ # rounded_value = TickRoundMethod.ROUND(value, tick_size=tick_size)
12
+ # assert rounded_value == Decimal("123.45")
13
13
 
14
- rounded_ceil = TickRoundMethod.CEIL(value, tick_size)
15
- assert rounded_ceil == Decimal("123.46")
14
+ # rounded_ceil = TickRoundMethod.CEIL(value, tick_size)
15
+ # assert rounded_ceil == Decimal("123.46")
16
16
 
17
- rounded_floor = TickRoundMethod.FLOOR(value, tick_size)
18
- assert rounded_floor == Decimal("123.45")
17
+ # rounded_floor = TickRoundMethod.FLOOR(value, tick_size)
18
+ # assert rounded_floor == Decimal("123.45")
19
19
 
20
- rounded_floor = TickRoundMethod.FLOOR(Decimal("123.459"), tick_size)
21
- assert rounded_floor == Decimal("123.45")
20
+ # rounded_floor = TickRoundMethod.FLOOR(Decimal("123.459"), tick_size)
21
+ # assert rounded_floor == Decimal("123.45")
22
22
 
23
- rounded_toward_zero_pos = TickRoundMethod.TOWARD_ZERO(value, tick_size)
24
- assert rounded_toward_zero_pos == Decimal("123.45")
23
+ # rounded_toward_zero_pos = TickRoundMethod.TOWARD_ZERO(value, tick_size)
24
+ # assert rounded_toward_zero_pos == Decimal("123.45")
25
25
 
26
- value_negative = Decimal("-123.456")
27
- rounded_toward_zero_neg = TickRoundMethod.TOWARD_ZERO(value_negative, tick_size)
28
- assert rounded_toward_zero_neg == Decimal("-123.45")
26
+ # value_negative = Decimal("-123.456")
27
+ # rounded_toward_zero_neg = TickRoundMethod.TOWARD_ZERO(value_negative, tick_size)
28
+ # assert rounded_toward_zero_neg == Decimal("-123.45")
29
29
 
30
- rounded_away_from_zero_pos = TickRoundMethod.AWAY_FROM_ZERO(value, tick_size)
31
- assert rounded_away_from_zero_pos == Decimal("123.46")
30
+ # rounded_away_from_zero_pos = TickRoundMethod.AWAY_FROM_ZERO(value, tick_size)
31
+ # assert rounded_away_from_zero_pos == Decimal("123.46")
32
32
 
33
- rounded_away_from_zero_neg = TickRoundMethod.AWAY_FROM_ZERO(
34
- value_negative, tick_size
35
- )
36
- assert rounded_away_from_zero_neg == Decimal("-123.46")
33
+ # rounded_away_from_zero_neg = TickRoundMethod.AWAY_FROM_ZERO(
34
+ # value_negative, tick_size
35
+ # )
36
+ # assert rounded_away_from_zero_neg == Decimal("-123.46")
37
37
 
38
38
 
39
- if __name__ == "__main__":
40
- test_rounding()
41
- print("rounding.py: All tests passed!")
39
+ # if __name__ == "__main__":
40
+ # test_rounding()
41
+ # print("rounding.py: All tests passed!")
@@ -2,32 +2,55 @@ import re
2
2
  from datetime import datetime
3
3
 
4
4
  import pytest
5
- from architect_py.async_client import AsyncClient
5
+
6
+ from architect_py import AsyncClient
6
7
 
7
8
 
8
9
  @pytest.mark.asyncio
9
- async def test_futures_series_populated(async_client: AsyncClient):
10
- # list of popular CME futures series and the minimum
11
- # number of futures we expect to see per series
10
+ async def test_list_symbols(async_client: AsyncClient):
11
+ symbols = await async_client.list_symbols()
12
+ assert len(symbols) > 0, "no symbols found"
13
+
14
+
15
+ @pytest.mark.asyncio
16
+ async def test_search_symbols_for_popular_CME_futures(async_client: AsyncClient):
17
+ """
18
+ Test that we have a minimum expected number of futures
19
+ for popular CME series.
20
+ """
12
21
  popular_series = [("ES", 5), ("GC", 5), ("NQ", 5)]
13
22
  for series, min_count in popular_series:
14
23
  markets = await async_client.search_symbols(
15
24
  execution_venue="CME", search_string=series
16
25
  )
17
26
  futures = [market for market in markets if market.startswith(f"{series} ")]
18
- assert (
19
- len(futures) > min_count
20
- ), f"not enough futures markets found in {series} series: {len(markets)}"
27
+ assert len(futures) > min_count, (
28
+ f"not enough futures markets found in {series} series: {len(markets)}"
29
+ )
30
+
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_cme_first_notice_date(async_client: AsyncClient):
34
+ # Find the nearest GC futures contract and check the first notice date
35
+ series_name = "GC CME Futures"
36
+ series = await async_client.get_futures_series(series_name)
37
+ assert len(series) > 0, "no futures markets found in GC series"
38
+
39
+ futures = await async_client.get_cme_futures_series(series_name)
40
+ exp_date, future = futures[0]
41
+ notice_date = await async_client.get_cme_first_notice_date(future)
42
+ assert notice_date is not None, "first notice date is None"
43
+ assert notice_date < exp_date, "first notice date is not before expiration"
21
44
 
22
45
 
23
46
  @pytest.mark.asyncio
24
- async def test_search_for_es_front_month(async_client: AsyncClient):
47
+ async def test_get_cme_futures_series(async_client: AsyncClient):
25
48
  series = await async_client.get_cme_futures_series("ES CME Futures")
26
49
  assert len(series) > 0, "no futures markets found in ES series"
27
50
  _, front_month_future = series[0]
28
- assert re.match(
29
- r"ES \d* CME Future", front_month_future
30
- ), "front month future base name does not match regex"
51
+ assert re.match(r"ES \d* CME Future", front_month_future), (
52
+ "front month future base name does not match regex"
53
+ )
31
54
 
32
55
 
33
56
  @pytest.mark.asyncio
@@ -35,16 +58,14 @@ async def test_get_cme_future_from_root_month_year(async_client: AsyncClient):
35
58
  # BTC futures are monthly. To avoid end-of-month weekend expiration,
36
59
  # check the next month from the current date.
37
60
  now = add_one_month_to_datetime(datetime.now())
38
-
39
61
  month = now.month
40
62
  year = now.year
41
63
  future = await async_client.get_cme_future_from_root_month_year(
42
64
  "BTC", month=month, year=year
43
65
  )
44
-
45
- assert re.match(
46
- f"BTC {year}{month:02d}[0-9]{{2}} CME Future", future
47
- ), "future base name does not match regex"
66
+ assert re.match(f"BTC {year}{month:02d}[0-9]{{2}} CME Future", future), (
67
+ "future base name does not match regex"
68
+ )
48
69
 
49
70
 
50
71
  def add_one_month_to_datetime(dt: datetime):
@@ -52,17 +73,3 @@ def add_one_month_to_datetime(dt: datetime):
52
73
  return dt.replace(year=dt.year + 1, month=1)
53
74
  else:
54
75
  return dt.replace(month=dt.month + 1)
55
-
56
-
57
- @pytest.mark.asyncio
58
- async def test_cme_first_notice_date(async_client: AsyncClient):
59
- # Find the nearest GC futures contract and check the first notice date
60
- series_name = "GC CME Futures"
61
- series = await async_client.get_future_series(series_name)
62
- assert len(series) > 0, "no futures markets found in GC series"
63
-
64
- futures = await async_client.get_cme_futures_series(series_name)
65
- exp_date, future = futures[0]
66
- notice_date = await async_client.get_cme_first_notice_date(future)
67
- assert notice_date is not None, "first notice date is None"
68
- assert notice_date < exp_date, "first notice date is not before expiration"
@@ -5,12 +5,10 @@ tick_size = get_tick_size(market_id, client)
5
5
  nearest_tick(123.456, TickRoundMethod.ROUND, tick_size)
6
6
  """
7
7
 
8
+ import sys
8
9
  from enum import Enum
9
-
10
10
  from functools import partial
11
11
 
12
- import sys
13
-
14
12
  """
15
13
  This conditional import is to deal with
16
14
  FutureWarning: functools.partial will be a method descriptor in future Python versions; wrap it in enum.member() if you want to preserve the old behavior
@@ -19,14 +17,13 @@ given on Python 3.12+
19
17
  if sys.version_info >= (3, 11):
20
18
  from .nearest_tick_2 import *
21
19
  else:
22
-
23
20
  from decimal import (
24
- Decimal,
25
21
  ROUND_CEILING,
26
22
  ROUND_DOWN,
27
23
  ROUND_FLOOR,
28
24
  ROUND_HALF_UP,
29
25
  ROUND_UP,
26
+ Decimal,
30
27
  )
31
28
 
32
29
  def round_method(value: Decimal, tick_size: Decimal) -> Decimal:
@@ -8,14 +8,13 @@ nearest_tick(123.456, TickRoundMethod.ROUND, tick_size)
8
8
  """
9
9
 
10
10
  from decimal import (
11
- Decimal,
12
11
  ROUND_CEILING,
13
12
  ROUND_DOWN,
14
13
  ROUND_FLOOR,
15
14
  ROUND_HALF_UP,
16
15
  ROUND_UP,
16
+ Decimal,
17
17
  )
18
-
19
18
  from enum import Enum, member
20
19
 
21
20
 
@@ -0,0 +1,35 @@
1
+ import bisect
2
+ from decimal import Decimal
3
+
4
+
5
+ def update_orderbook_side(
6
+ orderbook_side: list[list[Decimal]],
7
+ price: Decimal,
8
+ size: Decimal,
9
+ ascending: bool,
10
+ ) -> None:
11
+ """
12
+ Updates a sorted order list (either ascending for asks or descending for bids)
13
+ using binary search to insert, update, or remove the given price level.
14
+ """
15
+ if ascending:
16
+ idx = bisect.bisect_left(orderbook_side, [price, Decimal(0)])
17
+ else:
18
+ lo, hi = 0, len(orderbook_side)
19
+ while lo < hi:
20
+ mid = (lo + hi) // 2
21
+ if orderbook_side[mid][0] > price:
22
+ lo = mid + 1
23
+ else:
24
+ hi = mid
25
+ idx = lo
26
+
27
+ if idx < len(orderbook_side) and orderbook_side[idx][0] == price:
28
+ if size.is_zero():
29
+ orderbook_side.pop(idx)
30
+ else:
31
+ # Update the size.
32
+ orderbook_side[idx][1] = size
33
+ else:
34
+ if not size.is_zero():
35
+ orderbook_side.insert(idx, [price, size])
@@ -0,0 +1,44 @@
1
+ from typing import List
2
+
3
+ import msgspec
4
+ import pandas as pd
5
+
6
+ from ..grpc import Candle
7
+
8
+ CANDLES_FIELD_MAP = {
9
+ "av": "sell_volume",
10
+ "bv": "buy_volume",
11
+ "s": "symbol",
12
+ "v": "volume",
13
+ "w": "width",
14
+ "ac": "ask_close",
15
+ "ah": "ask_high",
16
+ "al": "ask_low",
17
+ "ao": "ask_open",
18
+ "bc": "bid_close",
19
+ "bh": "bid_high",
20
+ "bl": "bid_low",
21
+ "bo": "bid_open",
22
+ "c": "close",
23
+ "h": "high",
24
+ "l": "low",
25
+ "mc": "mid_close",
26
+ "mh": "mid_high",
27
+ "ml": "mid_low",
28
+ "mo": "mid_open",
29
+ "o": "open",
30
+ }
31
+
32
+
33
+ def candles_to_dataframe(candles: List[Candle]) -> pd.DataFrame:
34
+ records = msgspec.to_builtins(candles)
35
+ df = pd.DataFrame.from_records(records)
36
+ df.rename(columns=CANDLES_FIELD_MAP, inplace=True)
37
+ df["timestamp"] = pd.to_datetime(
38
+ df["ts"] * 1_000_000_000 + df["tn"],
39
+ unit="ns",
40
+ utc=True,
41
+ )
42
+ df.style.hide(["tn", "ts"], axis=1)
43
+ df.set_index("timestamp", inplace=True)
44
+ return df
@@ -73,7 +73,6 @@ price_band_pairs: dict[str, Decimal] = {
73
73
  "ZW": Decimal(0.0975),
74
74
  "XW": Decimal(0.0975),
75
75
  "CWD": Decimal(600),
76
- "CWD": Decimal(300),
77
76
  "ZWT": Decimal(0.02),
78
77
  "KE": Decimal(0.0975),
79
78
  "KWD": Decimal(600),
@@ -384,7 +383,6 @@ price_band_pairs: dict[str, Decimal] = {
384
383
  "XUB": Decimal(0.2),
385
384
  "ACB": Decimal(0.2),
386
385
  "AEB": Decimal(0.2),
387
- "FRC": Decimal(0.2),
388
386
  "TFB": Decimal(0.2),
389
387
  "USE": Decimal(0.2),
390
388
  "RBM": Decimal(2000),
@@ -481,7 +479,6 @@ price_band_pairs: dict[str, Decimal] = {
481
479
  "AUF": Decimal(0.001),
482
480
  "UV": Decimal(2),
483
481
  "AKR": Decimal(0.001),
484
- "AKR": Decimal(0.001),
485
482
  "EFF": Decimal(0.001),
486
483
  "AUI": Decimal(2),
487
484
  "AGT": Decimal(0.01),
@@ -0,0 +1,29 @@
1
+ """
2
+ Utility functions for decoding and understanding Architect symbols.
3
+ """
4
+
5
+ from datetime import date, datetime
6
+ from typing import Optional
7
+
8
+
9
+ def nominative_expiration(symbol: str) -> Optional[date]:
10
+ """
11
+ For futures and options symbols, extract the expiration date.
12
+
13
+ Args:
14
+ symbol: e.g. "ES 20211217 CME Future" -> date(2021, 12, 17)
15
+
16
+ Returns:
17
+ The expiration date as a date object
18
+ None if the symbol is not a future or option
19
+
20
+ To get a more precise expiration time or certain rare situations
21
+ involving timezone skews, use get_product_info from AsyncClient
22
+ or Client instead, which looks up actual product facts from the
23
+ symbology service.
24
+ """
25
+ try:
26
+ _, d, *_ = symbol.split(" ")
27
+ return datetime.strptime(d, "%Y%m%d").date()
28
+ except ValueError:
29
+ return None
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: architect-py
3
+ Version: 5.0.0
4
+ Summary: Python SDK for the Architect trading platform and brokerage.
5
+ Author-email: "Architect Financial Technologies, Inc." <hello@architect.co>
6
+ License-Expression: Apache-2.0
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Requires-Python: <4,>=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: asyncio>=3
14
+ Requires-Dist: gql[httpx]<4,>=3.5.0
15
+ Requires-Dist: grpcio>=1.66.1
16
+ Requires-Dist: msgspec<0.20,>=0.19
17
+ Requires-Dist: pandas>=2
18
+ Requires-Dist: pydantic~=2.10
19
+ Requires-Dist: websockets>=11
20
+ Requires-Dist: dnspython>=2.0
21
+ Dynamic: license-file
22
+
23
+ # [![Architect](https://avatars.githubusercontent.com/u/116864654?s=29&v=2)](https://architect.co) architect_py
24
+ [![PyPI version](https://img.shields.io/pypi/v/architect-py.svg)](https://pypi.org/project/architect-py/)
25
+
26
+ A fully-featured Python SDK for trading on [Architect](https://architect.co).
27
+
28
+ Just some of the features of this SDK: symbology, portfolio management, order entry, advanced algos, and marketdata subscriptions.
29
+
30
+ Also, it is compatible with Jupyter notebooks! Check the [examples for an example notebook](examples/jupyter_example.ipynb).
31
+
32
+ ## Installation
33
+
34
+ - pip: `pip install architect-py`
35
+ - poetry: `poetry add architect-py`
36
+ - uv: `uv add architect-py`
37
+
38
+ ## API keys for the brokerage
39
+
40
+ API keys/secrets for the brokerage can be generated on the [user account page](https://app.architect.co/user/account).
41
+
42
+
43
+ ## Documentation
44
+
45
+ See the [Getting started with Python](https://docs.architect.co/getting-started-with-python) guide for more information.
46
+
47
+
48
+ ## Method catalog
49
+
50
+ Go to [FUNCTIONS.md](FUNCTIONS.md) file to see a catalog of methods.
51
+
52
+ ---
53
+
54
+ ### Running examples from this package