architect-py 3.2.1__py3-none-any.whl → 5.0.0b1__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 +8 -2
- architect_py/async_client.py +887 -575
- architect_py/client.py +36 -33
- architect_py/client_interface.py +62 -0
- architect_py/common_types/__init__.py +6 -0
- architect_py/common_types/order_dir.py +85 -0
- architect_py/common_types/scalars.py +25 -0
- architect_py/common_types/tradable_product.py +59 -0
- architect_py/graphql_client/client.py +3 -6
- 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 +125 -0
- architect_py/grpc/client.py +86 -0
- architect_py/{grpc_client → grpc/models}/Accounts/AccountsRequest.py +2 -2
- 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_client → grpc/models}/Algo/AlgoOrderForTwapAlgo.py +1 -1
- architect_py/{grpc_client → grpc/models}/Algo/CreateAlgoOrderRequestForTwapAlgo.py +2 -2
- architect_py/{grpc_client → grpc/models}/Algo/ModifyAlgoOrderRequestForTwapAlgo.py +2 -2
- 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/Core/ConfigRequest.py +37 -0
- architect_py/grpc/models/Core/ConfigResponse.py +25 -0
- architect_py/{grpc_client/Folio → grpc/models/Core}/__init__.py +1 -1
- 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 +3 -3
- 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 +36 -3
- 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_client → grpc/models}/Oms/Cancel.py +1 -1
- 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 +2 -2
- 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 +3 -3
- architect_py/grpc/models/Oms/__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_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 +293 -223
- architect_py/grpc/resolve_endpoint.py +67 -0
- architect_py/{grpc_client/grpc_server.py → grpc/server.py} +13 -9
- architect_py/grpc/utils.py +32 -0
- architect_py/tests/conftest.py +86 -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 +38 -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.0b1.dist-info/METADATA +124 -0
- architect_py-5.0.0b1.dist-info/RECORD +184 -0
- {architect_py-3.2.1.dist-info → architect_py-5.0.0b1.dist-info}/WHEEL +2 -1
- architect_py-5.0.0b1.dist-info/top_level.txt +4 -0
- examples/__init__.py +0 -0
- examples/book_subscription.py +53 -0
- examples/candles.py +30 -0
- examples/common.py +107 -0
- examples/external_cpty.py +77 -0
- examples/funding_rate_mean_reversion_algo.py +192 -0
- examples/order_sending.py +92 -0
- examples/stream_l1_marketdata.py +25 -0
- examples/stream_l2_marketdata.py +40 -0
- examples/trades.py +21 -0
- examples/tutorial_async.py +84 -0
- examples/tutorial_sync.py +95 -0
- scripts/generate_functions_md.py +164 -0
- scripts/generate_sync_interface.py +207 -0
- scripts/postprocess_grpc.py +594 -0
- scripts/preprocess_grpc_schema.py +647 -0
- templates/exceptions.py +83 -0
- templates/juniper_base_client.py +371 -0
- architect_py/client_protocol.py +0 -52
- 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/__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.0b1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
from decimal import Decimal
|
4
|
+
|
5
|
+
from architect_py.async_client import AsyncClient
|
6
|
+
from architect_py.graphql_client.enums import OrderType, TimeInForce
|
7
|
+
from architect_py.scalars import OrderDir, TradableProduct
|
8
|
+
|
9
|
+
LOGGER = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
api_key = None
|
12
|
+
api_secret = None
|
13
|
+
HOST = None
|
14
|
+
ACCOUNT = None
|
15
|
+
|
16
|
+
|
17
|
+
if api_key is None or api_secret is None or HOST is None or ACCOUNT is None:
|
18
|
+
raise ValueError(
|
19
|
+
"Please set the api_key, api_secret, HOST, and ACCOUNT variables before running this script"
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
client = AsyncClient(host=HOST, api_key=api_key, api_secret=api_secret)
|
24
|
+
|
25
|
+
|
26
|
+
async def search_symbol() -> tuple[str, TradableProduct]:
|
27
|
+
venue = "CME"
|
28
|
+
markets = await client.search_symbols(
|
29
|
+
search_string="ES",
|
30
|
+
execution_venue=venue,
|
31
|
+
)
|
32
|
+
market = markets[0]
|
33
|
+
return venue, market
|
34
|
+
|
35
|
+
|
36
|
+
async def test_send_order():
|
37
|
+
venue, symbol = await search_symbol()
|
38
|
+
|
39
|
+
snapshot = await client.get_market_snapshot(symbol=symbol, venue=venue)
|
40
|
+
if snapshot is None:
|
41
|
+
return ValueError(f"Market snapshot for {symbol} is None")
|
42
|
+
|
43
|
+
if snapshot.ask_price is None or snapshot.bid_price is None:
|
44
|
+
return ValueError(f"Market snapshot for {symbol} is None")
|
45
|
+
|
46
|
+
order = await client.send_limit_order(
|
47
|
+
symbol=symbol,
|
48
|
+
odir=OrderDir.BUY,
|
49
|
+
quantity=Decimal(1),
|
50
|
+
order_type=OrderType.LIMIT,
|
51
|
+
execution_venue="CME",
|
52
|
+
post_only=True,
|
53
|
+
limit_price=snapshot.bid_price
|
54
|
+
- (snapshot.ask_price - snapshot.bid_price) * Decimal(10),
|
55
|
+
account=ACCOUNT,
|
56
|
+
time_in_force=TimeInForce.IOC,
|
57
|
+
)
|
58
|
+
logging.critical(f"ORDER TEST: {order}")
|
59
|
+
|
60
|
+
assert order is not None
|
61
|
+
|
62
|
+
cancel = await client.cancel_order(order.id)
|
63
|
+
|
64
|
+
return cancel
|
65
|
+
|
66
|
+
|
67
|
+
async def test_cancel_all_orders():
|
68
|
+
await client.cancel_all_orders()
|
69
|
+
|
70
|
+
|
71
|
+
async def test_send_market_pro_order():
|
72
|
+
venue, symbol = await search_symbol()
|
73
|
+
print(symbol)
|
74
|
+
|
75
|
+
await client.send_market_pro_order(
|
76
|
+
symbol=symbol,
|
77
|
+
execution_venue=venue,
|
78
|
+
odir=OrderDir.BUY,
|
79
|
+
quantity=Decimal(1),
|
80
|
+
account=ACCOUNT,
|
81
|
+
time_in_force=TimeInForce.IOC,
|
82
|
+
)
|
83
|
+
|
84
|
+
|
85
|
+
async def main():
|
86
|
+
await test_send_market_pro_order()
|
87
|
+
|
88
|
+
|
89
|
+
if __name__ == "__main__":
|
90
|
+
loop = asyncio.new_event_loop()
|
91
|
+
asyncio.set_event_loop(loop)
|
92
|
+
loop.run_until_complete(main())
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
from architect_py.async_client import AsyncClient
|
4
|
+
from architect_py.scalars import TradableProduct
|
5
|
+
|
6
|
+
from .common import connect_async_client
|
7
|
+
|
8
|
+
|
9
|
+
async def main():
|
10
|
+
c: AsyncClient = await connect_async_client()
|
11
|
+
|
12
|
+
async for snap in c.subscribe_l1_book_stream(
|
13
|
+
symbols=[TradableProduct("ES 20250620 CME Future/USD")],
|
14
|
+
venue="CME",
|
15
|
+
):
|
16
|
+
best_bid_s = "<no bid>"
|
17
|
+
best_ask_s = "<no ask>"
|
18
|
+
if snap.best_bid:
|
19
|
+
best_bid_s = f"{snap.best_bid[1]} x {snap.best_bid[0]}" # size x price
|
20
|
+
if snap.best_ask:
|
21
|
+
best_ask_s = f"{snap.best_ask[0]} x {snap.best_ask[1]}" # price x size
|
22
|
+
print(f"{snap.symbol} {snap.timestamp} {best_bid_s} {best_ask_s}")
|
23
|
+
|
24
|
+
|
25
|
+
asyncio.run(main())
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
from architect_py.async_client import AsyncClient
|
4
|
+
from architect_py.scalars import TradableProduct
|
5
|
+
|
6
|
+
from .common import connect_async_client
|
7
|
+
|
8
|
+
buy_columns = "{:>15} {:>15}"
|
9
|
+
sell_columns = "{:<15} {:<15}"
|
10
|
+
green = "\033[32m"
|
11
|
+
red = "\033[31m"
|
12
|
+
normal = "\033[0m"
|
13
|
+
|
14
|
+
|
15
|
+
async def print_l2_book(c: AsyncClient, symbol: TradableProduct, venue: str):
|
16
|
+
book = await c.subscribe_l2_book(symbol, venue)
|
17
|
+
while True:
|
18
|
+
print(f"book timestamp: {book.timestamp}")
|
19
|
+
print((buy_columns + " " + sell_columns).format("Size", "Bid", "Ask", "Size"))
|
20
|
+
for i in range(min(20, len(book.bids), len(book.asks))):
|
21
|
+
b = book.bids[i]
|
22
|
+
s = book.asks[i]
|
23
|
+
print(
|
24
|
+
(green + buy_columns).format(b[1], b[0]),
|
25
|
+
(red + sell_columns).format(s[0], s[1]),
|
26
|
+
)
|
27
|
+
print(normal)
|
28
|
+
await asyncio.sleep(1)
|
29
|
+
|
30
|
+
|
31
|
+
async def main():
|
32
|
+
c: AsyncClient = await connect_async_client()
|
33
|
+
endpoint = "app.architect.co" # one example of alternative can be "binance.marketdata.architect.co"
|
34
|
+
await c.grpc_client.change_channel(endpoint)
|
35
|
+
market_symbol = TradableProduct("ES 20250620 CME Future/USD")
|
36
|
+
venue = "CME"
|
37
|
+
await print_l2_book(c, market_symbol, venue=venue)
|
38
|
+
|
39
|
+
|
40
|
+
asyncio.run(main())
|
examples/trades.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
from architect_py.async_client import AsyncClient
|
4
|
+
from architect_py.graphql_client.exceptions import GraphQLClientHttpError
|
5
|
+
from architect_py.scalars import TradableProduct
|
6
|
+
|
7
|
+
from .common import connect_async_client
|
8
|
+
|
9
|
+
|
10
|
+
async def main():
|
11
|
+
c: AsyncClient = await connect_async_client()
|
12
|
+
market_id = TradableProduct("BTC Crypto", "USD")
|
13
|
+
try:
|
14
|
+
async for trade in c.subscribe_trades_stream(market_id, venue="COINBASE"):
|
15
|
+
print(trade)
|
16
|
+
except GraphQLClientHttpError as e:
|
17
|
+
print(e.status_code)
|
18
|
+
print(e.response.json())
|
19
|
+
|
20
|
+
|
21
|
+
asyncio.run(main())
|
@@ -0,0 +1,84 @@
|
|
1
|
+
import asyncio
|
2
|
+
from decimal import Decimal
|
3
|
+
|
4
|
+
from architect_py.async_client import OrderDir
|
5
|
+
from architect_py.graphql_client.enums import OrderStatus
|
6
|
+
from architect_py.scalars import TradableProduct
|
7
|
+
from examples.common import connect_async_client
|
8
|
+
|
9
|
+
|
10
|
+
async def main():
|
11
|
+
c = await connect_async_client()
|
12
|
+
|
13
|
+
market = TradableProduct("ES 20281215 CME Future/US")
|
14
|
+
execution_venue = "CME"
|
15
|
+
|
16
|
+
# Get market snapshot for a single market
|
17
|
+
# Market snapshots tell you the current best bid, best ask,
|
18
|
+
# and other ticker info for the given symbol.
|
19
|
+
print()
|
20
|
+
print(f"Market snapshot for {market}")
|
21
|
+
market_snapshot = await c.get_market_snapshot(symbol=market, venue=execution_venue)
|
22
|
+
print(f"Best bid: {market_snapshot.bid_price}")
|
23
|
+
print(f"Best ask: {market_snapshot.ask_price}")
|
24
|
+
|
25
|
+
# List your FCM accounts
|
26
|
+
print()
|
27
|
+
print("Your FCM accounts:")
|
28
|
+
accounts = await c.list_accounts()
|
29
|
+
for account in accounts:
|
30
|
+
print(f"{account.account.name}")
|
31
|
+
|
32
|
+
account_id = accounts[0].account.id
|
33
|
+
|
34
|
+
# Place a limit order $100 below the best bid
|
35
|
+
best_bid = market_snapshot.bid_price
|
36
|
+
if best_bid is None:
|
37
|
+
raise ValueError("No bid price available")
|
38
|
+
limit_price = best_bid - Decimal(100)
|
39
|
+
quantity = Decimal(1)
|
40
|
+
account = accounts[0]
|
41
|
+
order = None
|
42
|
+
print()
|
43
|
+
if (
|
44
|
+
input(
|
45
|
+
f"Place a limit order to BUY 1 LIMIT {limit_price} on account {account.account.name}? [y/N]"
|
46
|
+
)
|
47
|
+
== "y"
|
48
|
+
):
|
49
|
+
order = await c.send_limit_order(
|
50
|
+
symbol=market,
|
51
|
+
execution_venue=execution_venue,
|
52
|
+
odir=OrderDir.BUY,
|
53
|
+
quantity=quantity,
|
54
|
+
limit_price=limit_price,
|
55
|
+
account=str(account_id),
|
56
|
+
)
|
57
|
+
else:
|
58
|
+
raise ValueError("Order was not placed")
|
59
|
+
print(f"Order placed with ID: {order.id}")
|
60
|
+
|
61
|
+
# Poll order status until rejected or fully executed
|
62
|
+
# After 5 seconds, cancel the order
|
63
|
+
i = 0
|
64
|
+
while order.status is OrderStatus.OPEN:
|
65
|
+
await asyncio.sleep(1)
|
66
|
+
print(f"...order state: {order.status}")
|
67
|
+
order = await c.get_order(order.id)
|
68
|
+
assert order is not None
|
69
|
+
i += 1
|
70
|
+
if i == 5:
|
71
|
+
print("Canceling order")
|
72
|
+
await c.cancel_order(order.id)
|
73
|
+
|
74
|
+
if order.status is OrderStatus.REJECTED:
|
75
|
+
print(f"Order was rejected: {order.reject_reason}")
|
76
|
+
elif order.status is OrderStatus.CANCELED:
|
77
|
+
print("Order was canceled")
|
78
|
+
elif order.status is OrderStatus.OUT:
|
79
|
+
print(f"Order was filled for qty: {order.filled_quantity}")
|
80
|
+
print(f"Average execution price: {order.average_fill_price}")
|
81
|
+
|
82
|
+
|
83
|
+
if __name__ == "__main__":
|
84
|
+
asyncio.run(main())
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import pprint
|
2
|
+
import time
|
3
|
+
from decimal import Decimal
|
4
|
+
|
5
|
+
from architect_py.graphql_client.enums import OrderStatus
|
6
|
+
from architect_py.scalars import OrderDir
|
7
|
+
from architect_py.utils.nearest_tick import TickRoundMethod
|
8
|
+
|
9
|
+
from .common import confirm, connect_client, print_book, print_open_orders
|
10
|
+
|
11
|
+
c = connect_client()
|
12
|
+
|
13
|
+
venue = "CME"
|
14
|
+
|
15
|
+
# find ES markets
|
16
|
+
symbols = c.search_symbols(execution_venue=venue, search_string="ES")
|
17
|
+
print("\nFound markets:")
|
18
|
+
|
19
|
+
for s in symbols:
|
20
|
+
print(f" • {s}")
|
21
|
+
|
22
|
+
symbol = symbols[0]
|
23
|
+
|
24
|
+
# Lookup information about a single market
|
25
|
+
print(f"\nDetails for {symbols[0]}:")
|
26
|
+
product_info = c.get_product_info(symbol)
|
27
|
+
assert product_info is not None
|
28
|
+
pprint.pp(product_info)
|
29
|
+
|
30
|
+
# Get market snapshot for a single market
|
31
|
+
# Market snapshots tell you the current best bid, best ask,
|
32
|
+
# and other ticker info for the given symbol.
|
33
|
+
# this function is an alias for get_l1_book_snapshot
|
34
|
+
print(f"\nMarket snapshot for {product_info.symbol}:")
|
35
|
+
market_snapshot = c.get_market_snapshot(symbol=symbol, venue=venue)
|
36
|
+
|
37
|
+
pprint.pp(market_snapshot)
|
38
|
+
|
39
|
+
# Get L2 snapshot for a single market
|
40
|
+
print(f"\nL2 snapshot for {product_info.symbol}:")
|
41
|
+
start = time.perf_counter()
|
42
|
+
book = c.get_l2_book_snapshot(symbol=symbol, venue=venue)
|
43
|
+
elapsed = time.perf_counter() - start
|
44
|
+
print(f"Got book snapshot in {elapsed:.4f} seconds\n")
|
45
|
+
print_book(book)
|
46
|
+
|
47
|
+
# Get your accounts
|
48
|
+
print("\nYour accounts:")
|
49
|
+
accounts = c.list_accounts()
|
50
|
+
for account in accounts:
|
51
|
+
print(f" • {account.account.name}")
|
52
|
+
|
53
|
+
# Check your open orders
|
54
|
+
print("\nCurrent open orders:")
|
55
|
+
orders = c.get_open_orders()
|
56
|
+
print_open_orders(orders)
|
57
|
+
|
58
|
+
# Place a limit order 20% below the best bid
|
59
|
+
best_bid = market_snapshot.bid_price
|
60
|
+
assert best_bid is not None
|
61
|
+
limit_price = best_bid * Decimal(0.8)
|
62
|
+
quantity = Decimal(1)
|
63
|
+
account = accounts[0]
|
64
|
+
order = None
|
65
|
+
|
66
|
+
if confirm(
|
67
|
+
f"Place a limit order to BUY 1 {symbol} LIMIT {limit_price} on account {account.account.name}?"
|
68
|
+
):
|
69
|
+
order = c.send_limit_order(
|
70
|
+
symbol=symbol,
|
71
|
+
execution_venue=venue,
|
72
|
+
odir=OrderDir.BUY,
|
73
|
+
quantity=quantity,
|
74
|
+
limit_price=limit_price,
|
75
|
+
account=account.account.name,
|
76
|
+
price_round_method=TickRoundMethod.ROUND,
|
77
|
+
)
|
78
|
+
assert order is not None
|
79
|
+
print(f"\nOrder placed with ID: {order.id}")
|
80
|
+
|
81
|
+
# Poll order status until rejected or fully executed
|
82
|
+
while order.status is OrderStatus.OPEN:
|
83
|
+
time.sleep(1)
|
84
|
+
print(f"...order state: {order.status}")
|
85
|
+
order = c.get_order(order.id)
|
86
|
+
assert order is not None
|
87
|
+
|
88
|
+
# Print final order state
|
89
|
+
if order.status is OrderStatus.REJECTED:
|
90
|
+
print(f"Order was rejected: {order.reject_reason}")
|
91
|
+
elif order.status is OrderStatus.CANCELED:
|
92
|
+
print("Order was canceled")
|
93
|
+
elif order.status is OrderStatus.OUT:
|
94
|
+
print(f"Order was filled for qty: {order.filled_quantity}")
|
95
|
+
print(f"Average execution price: {order.average_fill_price}")
|
@@ -0,0 +1,164 @@
|
|
1
|
+
import ast
|
2
|
+
import re
|
3
|
+
import sys
|
4
|
+
from collections import defaultdict
|
5
|
+
|
6
|
+
|
7
|
+
def extract_sections(source_lines):
|
8
|
+
"""
|
9
|
+
Look for section header blocks that look like:
|
10
|
+
|
11
|
+
# ------------------------------------------------------------
|
12
|
+
# Some Section Name
|
13
|
+
# ------------------------------------------------------------
|
14
|
+
|
15
|
+
Returns a list of tuples (section_name, lineno) where lineno is the
|
16
|
+
line number (1-indexed) of the section title.
|
17
|
+
"""
|
18
|
+
sections = []
|
19
|
+
# Regex for a dashed line
|
20
|
+
dashed_re = re.compile(r"^\s*#\s*-{10,}\s*$")
|
21
|
+
# Regex for a section title line (should start with "#")
|
22
|
+
title_re = re.compile(r"^\s*#\s*(.+\S)\s*$")
|
23
|
+
|
24
|
+
i = 0
|
25
|
+
while i < len(source_lines):
|
26
|
+
if dashed_re.match(source_lines[i]):
|
27
|
+
if i + 1 < len(source_lines) and title_re.match(source_lines[i + 1]):
|
28
|
+
groups = title_re.match(source_lines[i + 1])
|
29
|
+
if groups is not None:
|
30
|
+
section_name = groups.group(1).strip()
|
31
|
+
else:
|
32
|
+
continue
|
33
|
+
# Check that the next line (i+2) is also a dashed line
|
34
|
+
if i + 2 < len(source_lines) and dashed_re.match(source_lines[i + 2]):
|
35
|
+
sections.append(
|
36
|
+
(section_name, i + 2)
|
37
|
+
) # record line number of closing dashed line, or i+1 as desired
|
38
|
+
i += 3
|
39
|
+
continue
|
40
|
+
i += 1
|
41
|
+
return sections
|
42
|
+
|
43
|
+
|
44
|
+
def find_section_for_lineno(lineno, sections):
|
45
|
+
"""
|
46
|
+
Given a line number (1-indexed) and a list of sections (name, lineno),
|
47
|
+
return the name of the last section that occurs before the given line number.
|
48
|
+
If none found, return "No Section".
|
49
|
+
"""
|
50
|
+
candidate = None
|
51
|
+
for name, sec_line in sections:
|
52
|
+
if sec_line < lineno:
|
53
|
+
candidate = name
|
54
|
+
else:
|
55
|
+
break
|
56
|
+
return candidate if candidate is not None else "No Section"
|
57
|
+
|
58
|
+
|
59
|
+
def get_asyncclient_methods(filename):
|
60
|
+
with open(filename, "r", encoding="utf-8") as f:
|
61
|
+
source = f.read()
|
62
|
+
source_lines = source.splitlines()
|
63
|
+
# Extract sections from source lines
|
64
|
+
sections = extract_sections(source_lines)
|
65
|
+
|
66
|
+
tree = ast.parse(source, filename)
|
67
|
+
methods = [] # list of tuples (section, func_name, doc_summary, lineno)
|
68
|
+
|
69
|
+
# Walk the AST to find the class AsyncClient
|
70
|
+
for node in ast.walk(tree):
|
71
|
+
if isinstance(node, ast.ClassDef) and node.name == "AsyncClient":
|
72
|
+
for item in node.body:
|
73
|
+
if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
74
|
+
# Exclude typing overloads
|
75
|
+
is_fn_overload = False
|
76
|
+
for decorator in item.decorator_list:
|
77
|
+
decorator_name = ""
|
78
|
+
if isinstance(decorator, ast.Call):
|
79
|
+
decorator_name = (
|
80
|
+
decorator.func.attr
|
81
|
+
if isinstance(decorator.func, ast.Attribute)
|
82
|
+
else decorator.func.id
|
83
|
+
)
|
84
|
+
else:
|
85
|
+
decorator_name = (
|
86
|
+
decorator.attr
|
87
|
+
if isinstance(decorator, ast.Attribute)
|
88
|
+
else decorator.id
|
89
|
+
)
|
90
|
+
if decorator_name == "overload":
|
91
|
+
is_fn_overload = True
|
92
|
+
|
93
|
+
if is_fn_overload:
|
94
|
+
continue
|
95
|
+
|
96
|
+
# Get docstring using ast.get_docstring
|
97
|
+
doc = ast.get_docstring(item)
|
98
|
+
if doc:
|
99
|
+
# Extract the first non-empty line from the docstring
|
100
|
+
first_line = next(
|
101
|
+
(line.strip() for line in doc.splitlines() if line.strip()),
|
102
|
+
"",
|
103
|
+
)
|
104
|
+
else:
|
105
|
+
first_line = ""
|
106
|
+
section = find_section_for_lineno(item.lineno, sections)
|
107
|
+
methods.append((section, item.name, first_line, item.lineno))
|
108
|
+
break # found our class, stop searching
|
109
|
+
return methods
|
110
|
+
|
111
|
+
|
112
|
+
def group_methods_by_section(methods):
|
113
|
+
grouped = defaultdict(list)
|
114
|
+
for section, name, summary, lineno in methods:
|
115
|
+
grouped[section].append((name, summary, lineno))
|
116
|
+
# Optionally sort sections by the lowest lineno
|
117
|
+
return dict(grouped)
|
118
|
+
|
119
|
+
|
120
|
+
def main(filename):
|
121
|
+
methods = get_asyncclient_methods(filename)
|
122
|
+
grouped = group_methods_by_section(methods)
|
123
|
+
# For predictable order, sort sections by the minimum line number among its methods
|
124
|
+
sorted_sections = sorted(
|
125
|
+
grouped.items(),
|
126
|
+
key=lambda item: min(m[2] for m in grouped[item[0]]) if grouped[item[0]] else 0,
|
127
|
+
)
|
128
|
+
# Alternatively, sort by section name alphabetically:
|
129
|
+
# sorted_sections = sorted(grouped.items())
|
130
|
+
for section, funcs in sorted_sections:
|
131
|
+
emoji = emoji_dict.get(section, "")
|
132
|
+
if emoji == "":
|
133
|
+
raise ValueError(f'Section "{section}" does not have an emoji.')
|
134
|
+
print(f"### {emoji} {section}")
|
135
|
+
print()
|
136
|
+
# Sort functions by their line number
|
137
|
+
for name, summary, lineno in sorted(funcs, key=lambda x: x[2]):
|
138
|
+
# Exclude private methods
|
139
|
+
if name.startswith("__"):
|
140
|
+
continue
|
141
|
+
# Only output if summary is non-empty
|
142
|
+
if summary:
|
143
|
+
if summary.startswith("@deprecated"):
|
144
|
+
continue
|
145
|
+
print(f"- **`{name}`**: {summary}")
|
146
|
+
else:
|
147
|
+
print(f"- **`{name}`**")
|
148
|
+
print("\n---\n")
|
149
|
+
|
150
|
+
|
151
|
+
emoji_dict: dict[str, str] = {
|
152
|
+
"Initialization and configuration": "🚀",
|
153
|
+
"Symbology": "🔍",
|
154
|
+
"Portfolio management": "💹",
|
155
|
+
"Order management": "📝",
|
156
|
+
"Order entry": "📣",
|
157
|
+
"Marketdata": "🧮",
|
158
|
+
}
|
159
|
+
|
160
|
+
if __name__ == "__main__":
|
161
|
+
if len(sys.argv) < 2:
|
162
|
+
print("Usage: python extract_methods.py <filename>")
|
163
|
+
else:
|
164
|
+
main(sys.argv[1])
|