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.
- architect_py/__init__.py +18 -2
- architect_py/async_client.py +1089 -658
- architect_py/client.py +36 -33
- architect_py/client_interface.py +63 -0
- architect_py/common_types/__init__.py +6 -0
- architect_py/common_types/order_dir.py +91 -0
- architect_py/common_types/scalars.py +25 -0
- architect_py/common_types/tradable_product.py +59 -0
- architect_py/graphql_client/__init__.py +2 -0
- architect_py/graphql_client/client.py +3 -6
- architect_py/graphql_client/enums.py +5 -0
- architect_py/graphql_client/fragments.py +3 -6
- architect_py/graphql_client/get_fills_query.py +2 -1
- architect_py/graphql_client/search_symbols_query.py +2 -1
- architect_py/graphql_client/subscribe_orderflow.py +2 -1
- architect_py/graphql_client/subscribe_trades.py +2 -1
- architect_py/grpc/__init__.py +145 -0
- architect_py/grpc/client.py +94 -0
- architect_py/{grpc_client → grpc/models}/Accounts/AccountsRequest.py +6 -3
- architect_py/{grpc_client → grpc/models}/Accounts/AccountsResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Accounts/__init__.py +1 -1
- architect_py/grpc/models/Algo/AlgoOrder.py +114 -0
- architect_py/grpc/models/Algo/AlgoOrderRequest.py +46 -0
- architect_py/grpc/models/Algo/AlgoOrdersRequest.py +72 -0
- architect_py/grpc/models/Algo/AlgoOrdersResponse.py +27 -0
- architect_py/grpc/models/Algo/CreateAlgoOrderRequest.py +56 -0
- architect_py/grpc/models/Algo/PauseAlgoRequest.py +42 -0
- architect_py/grpc/models/Algo/PauseAlgoResponse.py +20 -0
- architect_py/grpc/models/Algo/StartAlgoRequest.py +42 -0
- architect_py/grpc/models/Algo/StartAlgoResponse.py +20 -0
- architect_py/grpc/models/Algo/StopAlgoRequest.py +42 -0
- architect_py/grpc/models/Algo/StopAlgoResponse.py +20 -0
- architect_py/{grpc_client → grpc/models}/Algo/__init__.py +1 -1
- architect_py/grpc/models/Auth/CreateJwtRequest.py +47 -0
- architect_py/grpc/models/Auth/CreateJwtResponse.py +23 -0
- architect_py/{grpc_client/Cpty → grpc/models/Auth}/__init__.py +1 -1
- architect_py/grpc/models/Boss/DepositsRequest.py +40 -0
- architect_py/grpc/models/Boss/DepositsResponse.py +27 -0
- architect_py/grpc/models/Boss/RqdAccountStatisticsRequest.py +42 -0
- architect_py/grpc/models/Boss/RqdAccountStatisticsResponse.py +25 -0
- architect_py/grpc/models/Boss/StatementUrlRequest.py +40 -0
- architect_py/grpc/models/Boss/StatementUrlResponse.py +23 -0
- architect_py/grpc/models/Boss/StatementsRequest.py +40 -0
- architect_py/grpc/models/Boss/StatementsResponse.py +27 -0
- architect_py/grpc/models/Boss/WithdrawalsRequest.py +40 -0
- architect_py/grpc/models/Boss/WithdrawalsResponse.py +27 -0
- architect_py/{grpc_client/Folio → grpc/models/Boss}/__init__.py +1 -1
- architect_py/grpc/models/Core/ConfigRequest.py +37 -0
- architect_py/grpc/models/Core/ConfigResponse.py +25 -0
- architect_py/grpc/models/Core/__init__.py +2 -0
- architect_py/{grpc_client → grpc/models}/Cpty/CptyRequest.py +5 -4
- architect_py/{grpc_client → grpc/models}/Cpty/CptyResponse.py +6 -6
- architect_py/grpc/models/Cpty/CptyStatus.py +48 -0
- architect_py/grpc/models/Cpty/CptyStatusRequest.py +45 -0
- architect_py/grpc/models/Cpty/CptysRequest.py +37 -0
- architect_py/grpc/models/Cpty/CptysResponse.py +27 -0
- architect_py/grpc/models/Cpty/__init__.py +2 -0
- architect_py/{grpc_client → grpc/models}/Folio/AccountHistoryRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Folio/AccountHistoryResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Folio/AccountSummariesRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Folio/AccountSummariesResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Folio/AccountSummary.py +1 -1
- architect_py/{grpc_client → grpc/models}/Folio/AccountSummaryRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Folio/HistoricalFillsRequest.py +7 -4
- architect_py/{grpc_client → grpc/models}/Folio/HistoricalFillsResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Folio/HistoricalOrdersRequest.py +3 -3
- architect_py/{grpc_client → grpc/models}/Folio/HistoricalOrdersResponse.py +1 -1
- architect_py/grpc/models/Folio/__init__.py +2 -0
- architect_py/{grpc_client → grpc/models}/Health/HealthCheckRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Health/HealthCheckResponse.py +1 -1
- architect_py/grpc/models/Health/__init__.py +2 -0
- architect_py/{grpc_client → grpc/models}/Marketdata/Candle.py +1 -1
- architect_py/{grpc_client → grpc/models}/Marketdata/HistoricalCandlesRequest.py +11 -8
- architect_py/{grpc_client → grpc/models}/Marketdata/HistoricalCandlesResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Marketdata/L1BookSnapshot.py +52 -5
- architect_py/{grpc_client → grpc/models}/Marketdata/L1BookSnapshotRequest.py +8 -3
- architect_py/{grpc_client → grpc/models}/Marketdata/L1BookSnapshotsRequest.py +6 -3
- architect_py/{grpc_client → grpc/models}/Marketdata/L2BookSnapshot.py +1 -1
- architect_py/{grpc_client → grpc/models}/Marketdata/L2BookSnapshotRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/Liquidation.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/MarketStatus.py +1 -1
- architect_py/{grpc_client → grpc/models}/Marketdata/MarketStatusRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeCandlesRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeCurrentCandlesRequest.py +3 -4
- architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeL1BookSnapshotsRequest.py +6 -3
- architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeL2BookUpdatesRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeLiquidationsRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeManyCandlesRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeTickersRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/SubscribeTradesRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/Ticker.py +14 -3
- architect_py/{grpc_client → grpc/models}/Marketdata/TickerRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/TickersRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Marketdata/TickersResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Marketdata/Trade.py +2 -2
- architect_py/grpc/models/Marketdata/__init__.py +2 -0
- architect_py/grpc/models/Oms/Cancel.py +90 -0
- architect_py/{grpc_client → grpc/models}/Oms/CancelAllOrdersRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Oms/CancelAllOrdersResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Oms/CancelOrderRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Oms/OpenOrdersRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Oms/OpenOrdersResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Oms/Order.py +6 -13
- architect_py/{grpc_client → grpc/models}/Oms/PendingCancelsRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Oms/PendingCancelsResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Oms/PlaceOrderRequest.py +16 -23
- architect_py/grpc/models/Oms/__init__.py +2 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsChain.py +30 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsChainGreeks.py +30 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsChainGreeksRequest.py +47 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsChainRequest.py +45 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsExpirations.py +29 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsExpirationsRequest.py +42 -0
- architect_py/grpc/models/OptionsMarketdata/__init__.py +2 -0
- architect_py/{grpc_client → grpc/models}/Orderflow/DropcopyRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Orderflow/OrderflowRequest.py +2 -1
- architect_py/{grpc_client → grpc/models}/Orderflow/SubscribeOrderflowRequest.py +2 -2
- architect_py/grpc/models/Orderflow/__init__.py +2 -0
- architect_py/grpc/models/Symbology/DownloadProductCatalogRequest.py +42 -0
- architect_py/grpc/models/Symbology/DownloadProductCatalogResponse.py +27 -0
- architect_py/grpc/models/Symbology/ExecutionInfoRequest.py +47 -0
- architect_py/grpc/models/Symbology/ExecutionInfoResponse.py +27 -0
- architect_py/{grpc_client → grpc/models}/Symbology/PruneExpiredSymbolsRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Symbology/PruneExpiredSymbolsResponse.py +1 -1
- architect_py/{grpc_client → grpc/models}/Symbology/SubscribeSymbology.py +1 -1
- architect_py/{grpc_client → grpc/models}/Symbology/SymbologyRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Symbology/SymbologySnapshot.py +7 -2
- architect_py/{grpc_client → grpc/models}/Symbology/SymbologyUpdate.py +9 -2
- architect_py/{grpc_client → grpc/models}/Symbology/SymbolsRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Symbology/SymbolsResponse.py +1 -1
- architect_py/grpc/models/Symbology/UploadProductCatalogRequest.py +49 -0
- architect_py/grpc/models/Symbology/UploadProductCatalogResponse.py +20 -0
- architect_py/{grpc_client → grpc/models}/Symbology/UploadSymbologyRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Symbology/UploadSymbologyResponse.py +1 -1
- architect_py/grpc/models/Symbology/__init__.py +2 -0
- architect_py/grpc/models/__init__.py +2 -0
- architect_py/{grpc_client → grpc/models}/definitions.py +671 -934
- architect_py/grpc/resolve_endpoint.py +70 -0
- architect_py/{grpc_client/grpc_server.py → grpc/server.py} +13 -9
- architect_py/grpc/utils.py +32 -0
- architect_py/internal_utils/__init__.py +0 -0
- architect_py/internal_utils/no_pandas.py +3 -0
- architect_py/tests/conftest.py +91 -85
- architect_py/tests/test_book_building.py +49 -50
- architect_py/tests/test_marketdata.py +168 -0
- architect_py/tests/test_order_entry.py +37 -0
- architect_py/tests/test_orderflow.py +41 -0
- architect_py/tests/test_portfolio_management.py +23 -0
- architect_py/tests/test_rounding.py +28 -28
- architect_py/tests/test_symbology.py +37 -30
- architect_py/utils/nearest_tick.py +2 -5
- architect_py/utils/nearest_tick_2.py +1 -2
- architect_py/utils/orderbook.py +35 -0
- architect_py/utils/pandas.py +44 -0
- architect_py/utils/price_bands.py +0 -3
- architect_py/utils/symbol_parsing.py +29 -0
- architect_py-5.0.0.dist-info/METADATA +54 -0
- architect_py-5.0.0.dist-info/RECORD +214 -0
- {architect_py-3.2.1.dist-info → architect_py-5.0.0.dist-info}/WHEEL +2 -1
- architect_py-5.0.0.dist-info/top_level.txt +4 -0
- examples/__init__.py +0 -0
- examples/book_subscription.py +52 -0
- examples/candles.py +30 -0
- examples/common.py +116 -0
- examples/external_cpty.py +77 -0
- examples/funding_rate_mean_reversion_algo.py +186 -0
- examples/order_sending.py +91 -0
- examples/stream_l1_marketdata.py +25 -0
- examples/stream_l2_marketdata.py +38 -0
- examples/trades.py +21 -0
- examples/tutorial_async.py +86 -0
- examples/tutorial_sync.py +95 -0
- scripts/generate_functions_md.py +166 -0
- scripts/generate_sync_interface.py +226 -0
- scripts/postprocess_grpc.py +604 -0
- scripts/preprocess_grpc_schema.py +708 -0
- templates/exceptions.py +83 -0
- templates/juniper_base_client.py +371 -0
- architect_py/client_protocol.py +0 -52
- architect_py/grpc_client/Algo/AlgoOrderForTwapAlgo.py +0 -61
- architect_py/grpc_client/Algo/CreateAlgoOrderRequestForTwapAlgo.py +0 -59
- architect_py/grpc_client/Algo/ModifyAlgoOrderRequestForTwapAlgo.py +0 -45
- architect_py/grpc_client/Folio/AggregatedAccountSummariesRequest.py +0 -59
- architect_py/grpc_client/Folio/AggregatedAccountSummariesResponse.py +0 -27
- architect_py/grpc_client/Health/__init__.py +0 -2
- architect_py/grpc_client/Marketdata/__init__.py +0 -2
- architect_py/grpc_client/Oms/Cancel.py +0 -42
- architect_py/grpc_client/Oms/__init__.py +0 -2
- architect_py/grpc_client/Orderflow/__init__.py +0 -2
- architect_py/grpc_client/Symbology/__init__.py +0 -2
- architect_py/grpc_client/__init__.py +0 -2
- architect_py/grpc_client/grpc_client.py +0 -405
- architect_py/scalars.py +0 -172
- architect_py/tests/test_accounts.py +0 -31
- architect_py/tests/test_client.py +0 -29
- architect_py/tests/test_grpc_client.py +0 -30
- architect_py/tests/test_order_sending.py +0 -61
- architect_py/tests/test_snapshots.py +0 -52
- architect_py/tests/test_subscriptions.py +0 -129
- architect_py-3.2.1.dist-info/METADATA +0 -212
- architect_py-3.2.1.dist-info/RECORD +0 -146
- /architect_py/{grpc_client → grpc/models}/Marketdata/ArrayOfL1BookSnapshot.py +0 -0
- /architect_py/{grpc_client → grpc/models}/Marketdata/L2BookUpdate.py +0 -0
- /architect_py/{grpc_client → grpc/models}/Marketdata/TickerUpdate.py +0 -0
- /architect_py/{grpc_client → grpc/models}/Orderflow/Dropcopy.py +0 -0
- /architect_py/{grpc_client → grpc/models}/Orderflow/Orderflow.py +0 -0
- {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
|
-
|
8
|
-
|
9
|
-
|
6
|
+
# def test_rounding():
|
7
|
+
# # Example usage
|
8
|
+
# value = Decimal("123.454")
|
9
|
+
# tick_size = Decimal("0.01")
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
# rounded_value = TickRoundMethod.ROUND(value, tick_size=tick_size)
|
12
|
+
# assert rounded_value == Decimal("123.45")
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
# rounded_ceil = TickRoundMethod.CEIL(value, tick_size)
|
15
|
+
# assert rounded_ceil == Decimal("123.46")
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
# rounded_floor = TickRoundMethod.FLOOR(value, tick_size)
|
18
|
+
# assert rounded_floor == Decimal("123.45")
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
# rounded_floor = TickRoundMethod.FLOOR(Decimal("123.459"), tick_size)
|
21
|
+
# assert rounded_floor == Decimal("123.45")
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
# rounded_toward_zero_pos = TickRoundMethod.TOWARD_ZERO(value, tick_size)
|
24
|
+
# assert rounded_toward_zero_pos == Decimal("123.45")
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
31
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
5
|
+
|
6
|
+
from architect_py import AsyncClient
|
6
7
|
|
7
8
|
|
8
9
|
@pytest.mark.asyncio
|
9
|
-
async def
|
10
|
-
|
11
|
-
|
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(
|
20
|
-
)
|
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
|
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
|
-
|
30
|
-
)
|
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
|
-
|
46
|
-
|
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:
|
@@ -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
|
+
# [](https://architect.co) architect_py
|
24
|
+
[](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
|