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.
- architect_py/__init__.py +15 -2
- architect_py/async_client.py +1060 -643
- architect_py/client.py +25 -26
- 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 +143 -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 +2 -2
- architect_py/{grpc_client → grpc/models}/Cpty/CptyResponse.py +3 -3
- architect_py/{grpc_client → grpc/models}/Cpty/CptyStatus.py +1 -1
- architect_py/{grpc_client → grpc/models}/Cpty/CptyStatusRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Cpty/CptysRequest.py +2 -2
- architect_py/{grpc_client → grpc/models}/Cpty/CptysResponse.py +1 -1
- 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 +1 -1
- 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 +1 -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 +690 -841
- architect_py/grpc/resolve_endpoint.py +70 -0
- architect_py/{grpc_client/grpc_server.py → grpc/server.py} +9 -6
- 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 -87
- 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.0b2.dist-info/METADATA +123 -0
- architect_py-5.0.0b2.dist-info/RECORD +214 -0
- {architect_py-3.2.2.dist-info → architect_py-5.0.0b2.dist-info}/WHEEL +2 -1
- architect_py-5.0.0b2.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 -53
- 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/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 -413
- 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 -65
- architect_py/tests/test_snapshots.py +0 -52
- architect_py/tests/test_subscriptions.py +0 -126
- architect_py-3.2.2.dist-info/METADATA +0 -191
- architect_py-3.2.2.dist-info/RECORD +0 -148
- /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.2.dist-info → architect_py-5.0.0b2.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,186 @@
|
|
1
|
+
"""
|
2
|
+
Do *NOT* run this script in production. This script is for educational purposes only.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import asyncio
|
6
|
+
from decimal import Decimal
|
7
|
+
from typing import AsyncIterator, Optional
|
8
|
+
|
9
|
+
from architect_py.async_client import AsyncClient
|
10
|
+
from architect_py.common_types.order_dir import OrderDir
|
11
|
+
from architect_py.common_types.tradable_product import TradableProduct
|
12
|
+
from architect_py.graphql_client.exceptions import GraphQLClientHttpError
|
13
|
+
from architect_py.grpc.models.definitions import OrderType, TimeInForceEnum
|
14
|
+
from architect_py.grpc.models.Orderflow.Orderflow import (
|
15
|
+
TaggedOrderAck,
|
16
|
+
TaggedOrderOut,
|
17
|
+
TaggedOrderReject,
|
18
|
+
)
|
19
|
+
from architect_py.grpc.models.Orderflow.OrderflowRequest import (
|
20
|
+
OrderflowRequest,
|
21
|
+
PlaceOrder,
|
22
|
+
)
|
23
|
+
|
24
|
+
from .common import connect_async_client
|
25
|
+
|
26
|
+
venue = "BINANCE"
|
27
|
+
product = "BTC-USDT Perpetual"
|
28
|
+
tradable_product = TradableProduct(product, "USDT Crypto")
|
29
|
+
best_bid_price: Optional[Decimal] = None
|
30
|
+
best_ask_price: Optional[Decimal] = None
|
31
|
+
current_funding_rate: Optional[Decimal] = None # as fraction, e.g. 0.0001 = 1 bp
|
32
|
+
target_position = 0
|
33
|
+
current_position = 0
|
34
|
+
|
35
|
+
|
36
|
+
class OrderflowRequester:
|
37
|
+
def __init__(self):
|
38
|
+
self.queue: asyncio.Queue[OrderflowRequest] = asyncio.Queue()
|
39
|
+
|
40
|
+
async def __aiter__(self) -> AsyncIterator[OrderflowRequest]:
|
41
|
+
while True:
|
42
|
+
value = await self.queue.get() # Wait for a value
|
43
|
+
yield value # Yield it when available
|
44
|
+
|
45
|
+
async def __anext__(self) -> OrderflowRequest:
|
46
|
+
return await self.queue.get()
|
47
|
+
|
48
|
+
async def put(self, value: OrderflowRequest) -> None:
|
49
|
+
# OrderflowRequest contains: PlaceOrder, CancelOrder, CancelAllOrders
|
50
|
+
await self.queue.put(value) # Put value into the queue
|
51
|
+
|
52
|
+
|
53
|
+
async def update_marketdata(c: AsyncClient):
|
54
|
+
while True:
|
55
|
+
ticker = await c.get_ticker(tradable_product, venue)
|
56
|
+
if ticker.funding_rate:
|
57
|
+
global current_funding_rate
|
58
|
+
global target_position
|
59
|
+
current_funding_rate = ticker.funding_rate
|
60
|
+
# set target_position based on funding rate
|
61
|
+
if current_funding_rate >= 0.1:
|
62
|
+
target_position = -10
|
63
|
+
elif current_funding_rate >= 0.05:
|
64
|
+
target_position = -5
|
65
|
+
elif current_funding_rate >= 0.0001:
|
66
|
+
target_position = 1
|
67
|
+
elif current_funding_rate >= -0.05:
|
68
|
+
target_position = 5
|
69
|
+
else:
|
70
|
+
target_position = 10
|
71
|
+
if ticker.bid_price:
|
72
|
+
global best_bid_price
|
73
|
+
best_bid_price = ticker.bid_price
|
74
|
+
if ticker.ask_price:
|
75
|
+
global best_ask_price
|
76
|
+
best_ask_price = ticker.ask_price
|
77
|
+
await asyncio.sleep(1)
|
78
|
+
|
79
|
+
|
80
|
+
async def subscribe_and_print_orderflow(
|
81
|
+
c: AsyncClient, orderflow_requester: OrderflowRequester
|
82
|
+
):
|
83
|
+
try:
|
84
|
+
stream = c.orderflow(orderflow_requester)
|
85
|
+
"""
|
86
|
+
subscribe_orderflow_stream is a duplex_stream meaning that it is a stream that can be read from and written to.
|
87
|
+
This is a stream that will be used to send orders to the Architect and receive order updates from the Architect.
|
88
|
+
"""
|
89
|
+
async for orderflow in stream:
|
90
|
+
if isinstance(orderflow, TaggedOrderAck):
|
91
|
+
print(f"<!> ACK {orderflow.order_id}")
|
92
|
+
if isinstance(orderflow, TaggedOrderReject):
|
93
|
+
print(f"<!> REJECT {orderflow.id} {orderflow.r}: {orderflow.rm}")
|
94
|
+
# reject reason, reject message
|
95
|
+
elif isinstance(orderflow, TaggedOrderOut):
|
96
|
+
print(f"<!> OUT {orderflow.id}")
|
97
|
+
except GraphQLClientHttpError as e:
|
98
|
+
print(e.status_code)
|
99
|
+
print(e.response.json())
|
100
|
+
|
101
|
+
|
102
|
+
async def step_to_target_position(
|
103
|
+
c: AsyncClient, orderflow_requester: OrderflowRequester
|
104
|
+
):
|
105
|
+
while True:
|
106
|
+
await asyncio.sleep(10)
|
107
|
+
# check open orders
|
108
|
+
open_orders = await c.get_open_orders()
|
109
|
+
n = len(open_orders)
|
110
|
+
if n > 0:
|
111
|
+
print("there are {n} open orders, skipping step")
|
112
|
+
continue
|
113
|
+
|
114
|
+
order = None
|
115
|
+
# send orders to step to target position
|
116
|
+
if current_position < target_position:
|
117
|
+
if best_ask_price is not None:
|
118
|
+
# buy 1 contract
|
119
|
+
|
120
|
+
# make sure to import the correct PlaceOrder from architect_py.grpc_client.Orderflow.OrderflowRequest
|
121
|
+
order = PlaceOrder.new(
|
122
|
+
symbol=tradable_product,
|
123
|
+
dir=OrderDir.BUY,
|
124
|
+
quantity=Decimal(1),
|
125
|
+
execution_venue=None,
|
126
|
+
limit_price=best_ask_price,
|
127
|
+
time_in_force=TimeInForceEnum.DAY,
|
128
|
+
order_type=OrderType.LIMIT,
|
129
|
+
)
|
130
|
+
|
131
|
+
elif current_position > target_position:
|
132
|
+
if best_bid_price is not None:
|
133
|
+
# sell 1 contract
|
134
|
+
|
135
|
+
# make sure to import the correct PlaceOrder from architect_py.grpc_client.Orderflow.OrderflowRequest
|
136
|
+
order = PlaceOrder.new(
|
137
|
+
symbol=tradable_product,
|
138
|
+
dir=OrderDir.SELL,
|
139
|
+
quantity=Decimal(1),
|
140
|
+
execution_venue=None,
|
141
|
+
limit_price=best_bid_price,
|
142
|
+
time_in_force=TimeInForceEnum.DAY,
|
143
|
+
order_type=OrderType.LIMIT,
|
144
|
+
)
|
145
|
+
|
146
|
+
if order is not None:
|
147
|
+
await orderflow_requester.put(
|
148
|
+
order
|
149
|
+
) # this will add the order to the queue to send over
|
150
|
+
|
151
|
+
|
152
|
+
async def print_info(c: AsyncClient):
|
153
|
+
accounts = await c.list_accounts()
|
154
|
+
while True:
|
155
|
+
await asyncio.sleep(3)
|
156
|
+
account_summaries = await c.get_account_summaries(
|
157
|
+
accounts=[account.account.name for account in accounts]
|
158
|
+
)
|
159
|
+
pos = Decimal(0)
|
160
|
+
for account in account_summaries:
|
161
|
+
for name, balance in account.balances.items():
|
162
|
+
print(f"balance for {name}: {balance}")
|
163
|
+
pos += balance
|
164
|
+
global current_position
|
165
|
+
current_position = pos
|
166
|
+
print("---")
|
167
|
+
print(f"info : funding_rate: {current_funding_rate}")
|
168
|
+
print(f"info : bbo: {best_bid_price} {best_ask_price}")
|
169
|
+
print(f"info : current_position: {current_position}")
|
170
|
+
print(f"info : target_position: {target_position}")
|
171
|
+
|
172
|
+
|
173
|
+
async def main():
|
174
|
+
c = await connect_async_client()
|
175
|
+
|
176
|
+
orderflow_requester = OrderflowRequester()
|
177
|
+
|
178
|
+
await asyncio.gather(
|
179
|
+
update_marketdata(c),
|
180
|
+
step_to_target_position(c, orderflow_requester),
|
181
|
+
print_info(c),
|
182
|
+
subscribe_and_print_orderflow(c, orderflow_requester),
|
183
|
+
)
|
184
|
+
|
185
|
+
|
186
|
+
asyncio.run(main())
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
from datetime import datetime, timedelta, timezone
|
4
|
+
from decimal import Decimal
|
5
|
+
|
6
|
+
from architect_py.async_client import AsyncClient
|
7
|
+
from architect_py.common_types.order_dir import OrderDir
|
8
|
+
from architect_py.common_types.tradable_product import TradableProduct
|
9
|
+
from architect_py.grpc.models.definitions import GoodTilDate, OrderType, TimeInForceEnum
|
10
|
+
from examples.common import connect_async_client
|
11
|
+
|
12
|
+
LOGGER = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
async def search_symbol(c: AsyncClient) -> tuple[str, TradableProduct]:
|
16
|
+
venue = "CME"
|
17
|
+
markets = await c.search_symbols(
|
18
|
+
search_string="ES",
|
19
|
+
execution_venue=venue,
|
20
|
+
)
|
21
|
+
market = markets[0]
|
22
|
+
return venue, market
|
23
|
+
|
24
|
+
|
25
|
+
async def test_send_order(client: AsyncClient, account: str):
|
26
|
+
venue, symbol = await search_symbol(client)
|
27
|
+
|
28
|
+
snapshot = await client.get_market_snapshot(symbol=symbol, venue=venue)
|
29
|
+
if snapshot is None:
|
30
|
+
return ValueError(f"Market snapshot for {symbol} is None")
|
31
|
+
|
32
|
+
if snapshot.best_ask is None or snapshot.best_bid is None:
|
33
|
+
return ValueError(f"Market snapshot for {symbol} is None")
|
34
|
+
|
35
|
+
best_bid_price, best_bid_quantity = snapshot.best_bid
|
36
|
+
|
37
|
+
d = datetime.now(tz=timezone.utc) + timedelta(days=1)
|
38
|
+
gtd = GoodTilDate(d)
|
39
|
+
|
40
|
+
order = await client.place_limit_order(
|
41
|
+
symbol=symbol,
|
42
|
+
odir=OrderDir.BUY,
|
43
|
+
quantity=best_bid_quantity,
|
44
|
+
order_type=OrderType.LIMIT,
|
45
|
+
execution_venue="CME",
|
46
|
+
post_only=True,
|
47
|
+
limit_price=best_bid_price
|
48
|
+
- (snapshot.best_ask[0] - best_bid_price) * Decimal(10),
|
49
|
+
account=account,
|
50
|
+
time_in_force=gtd,
|
51
|
+
)
|
52
|
+
logging.critical(f"ORDER TEST: {order}")
|
53
|
+
|
54
|
+
assert order is not None
|
55
|
+
|
56
|
+
cancel = await client.cancel_order(order.id)
|
57
|
+
|
58
|
+
return cancel
|
59
|
+
|
60
|
+
|
61
|
+
async def test_cancel_all_orders(client: AsyncClient):
|
62
|
+
await client.cancel_all_orders()
|
63
|
+
|
64
|
+
|
65
|
+
async def test_send_market_pro_order(client: AsyncClient, account: str):
|
66
|
+
venue, symbol = await search_symbol(client)
|
67
|
+
print(symbol)
|
68
|
+
|
69
|
+
await client.send_market_pro_order(
|
70
|
+
symbol=symbol,
|
71
|
+
execution_venue=venue,
|
72
|
+
odir=OrderDir.BUY,
|
73
|
+
quantity=Decimal(1),
|
74
|
+
account=account,
|
75
|
+
time_in_force=TimeInForceEnum.IOC,
|
76
|
+
)
|
77
|
+
|
78
|
+
|
79
|
+
async def main():
|
80
|
+
client: AsyncClient = await connect_async_client()
|
81
|
+
accounts = await client.list_accounts()
|
82
|
+
account: str = accounts[0].account.name
|
83
|
+
|
84
|
+
await test_send_order(client, account)
|
85
|
+
await test_send_market_pro_order(client, account)
|
86
|
+
|
87
|
+
|
88
|
+
if __name__ == "__main__":
|
89
|
+
loop = asyncio.new_event_loop()
|
90
|
+
asyncio.set_event_loop(loop)
|
91
|
+
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.common_types.tradable_product 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.stream_l1_book_snapshots(
|
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,38 @@
|
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
from architect_py.async_client import AsyncClient
|
4
|
+
from architect_py.common_types.tradable_product 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
|
+
market_symbol = TradableProduct("ES 20250620 CME Future/USD")
|
34
|
+
venue = "CME"
|
35
|
+
await print_l2_book(c, market_symbol, venue=venue)
|
36
|
+
|
37
|
+
|
38
|
+
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.common_types.tradable_product import TradableProduct
|
5
|
+
from architect_py.graphql_client.exceptions import GraphQLClientHttpError
|
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.stream_trades(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,86 @@
|
|
1
|
+
import asyncio
|
2
|
+
from decimal import Decimal
|
3
|
+
|
4
|
+
from architect_py.async_client import OrderDir
|
5
|
+
from architect_py.common_types.tradable_product import TradableProduct
|
6
|
+
from architect_py.graphql_client.enums import OrderStatus
|
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.best_bid}")
|
23
|
+
print(f"Best ask: {market_snapshot.best_ask}")
|
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
|
+
if (best_bid := market_snapshot.best_bid) is not None:
|
36
|
+
bid_px = best_bid[0]
|
37
|
+
else:
|
38
|
+
raise ValueError("No bid price available")
|
39
|
+
|
40
|
+
limit_price = bid_px - Decimal(100)
|
41
|
+
quantity = Decimal(1)
|
42
|
+
account = accounts[0]
|
43
|
+
order = None
|
44
|
+
print()
|
45
|
+
if (
|
46
|
+
input(
|
47
|
+
f"Place a limit order to BUY 1 LIMIT {limit_price} on account {account.account.name}? [y/N]"
|
48
|
+
)
|
49
|
+
== "y"
|
50
|
+
):
|
51
|
+
order = await c.place_limit_order(
|
52
|
+
symbol=market,
|
53
|
+
execution_venue=execution_venue,
|
54
|
+
odir=OrderDir.BUY,
|
55
|
+
quantity=quantity,
|
56
|
+
limit_price=limit_price,
|
57
|
+
account=str(account_id),
|
58
|
+
)
|
59
|
+
else:
|
60
|
+
raise ValueError("Order was not placed")
|
61
|
+
print(f"Order placed with ID: {order.id}")
|
62
|
+
|
63
|
+
# Poll order status until rejected or fully executed
|
64
|
+
# After 5 seconds, cancel the order
|
65
|
+
i = 0
|
66
|
+
while order.status is OrderStatus.OPEN:
|
67
|
+
await asyncio.sleep(1)
|
68
|
+
print(f"...order state: {order.status}")
|
69
|
+
order = await c.get_order(order.id)
|
70
|
+
assert order is not None
|
71
|
+
i += 1
|
72
|
+
if i == 5:
|
73
|
+
print("Canceling order")
|
74
|
+
await c.cancel_order(order.id)
|
75
|
+
|
76
|
+
if order.status is OrderStatus.REJECTED:
|
77
|
+
print(f"Order was rejected: {order.reject_reason}")
|
78
|
+
elif order.status is OrderStatus.CANCELED:
|
79
|
+
print("Order was canceled")
|
80
|
+
elif order.status is OrderStatus.OUT:
|
81
|
+
print(f"Order was filled for qty: {order.filled_quantity}")
|
82
|
+
print(f"Average execution price: {order.average_fill_price}")
|
83
|
+
|
84
|
+
|
85
|
+
if __name__ == "__main__":
|
86
|
+
asyncio.run(main())
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import pprint
|
2
|
+
import time
|
3
|
+
from decimal import Decimal
|
4
|
+
|
5
|
+
from architect_py.common_types.order_dir import OrderDir
|
6
|
+
from architect_py.graphql_client.enums import OrderStatus
|
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.best_bid
|
60
|
+
assert best_bid is not None
|
61
|
+
best_bid_price, best_bid_quantity = best_bid
|
62
|
+
limit_price = best_bid_price * Decimal(0.8)
|
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=best_bid_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}")
|