architect-py 5.1.5__py3-none-any.whl → 5.2.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 +24 -4
- architect_py/async_client.py +66 -57
- architect_py/async_cpty.py +422 -0
- architect_py/client.pyi +2 -34
- architect_py/grpc/models/Accounts/ResetPaperAccountRequest.py +59 -0
- architect_py/grpc/models/Accounts/ResetPaperAccountResponse.py +20 -0
- architect_py/grpc/models/Boss/OptionsTransactionsRequest.py +42 -0
- architect_py/grpc/models/Boss/OptionsTransactionsResponse.py +27 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsChain.py +5 -5
- architect_py/grpc/models/OptionsMarketdata/OptionsChainGreeks.py +5 -5
- architect_py/grpc/models/OptionsMarketdata/OptionsChainGreeksRequest.py +5 -1
- architect_py/grpc/models/OptionsMarketdata/OptionsChainRequest.py +5 -1
- architect_py/grpc/models/OptionsMarketdata/OptionsContract.py +45 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsContractGreeksRequest.py +40 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsContractRequest.py +40 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsExpirations.py +4 -1
- architect_py/grpc/models/OptionsMarketdata/OptionsExpirationsRequest.py +8 -1
- architect_py/grpc/models/OptionsMarketdata/OptionsGreeks.py +58 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsWraps.py +28 -0
- architect_py/grpc/models/OptionsMarketdata/OptionsWrapsRequest.py +40 -0
- architect_py/grpc/models/__init__.py +11 -1
- architect_py/grpc/models/definitions.py +37 -86
- architect_py/grpc/orderflow.py +3 -7
- architect_py/grpc/server.py +1 -3
- architect_py/tests/test_order_entry.py +120 -1
- architect_py/tests/test_positions.py +208 -17
- {architect_py-5.1.5.dist-info → architect_py-5.2.0.dist-info}/METADATA +1 -1
- {architect_py-5.1.5.dist-info → architect_py-5.2.0.dist-info}/RECORD +41 -30
- examples/external_cpty.py +72 -66
- examples/funding_rate_mean_reversion_algo.py +4 -4
- examples/order_sending.py +3 -3
- examples/orderflow_channel.py +75 -56
- examples/stream_l1_marketdata.py +3 -1
- examples/stream_l2_marketdata.py +3 -1
- examples/tutorial_async.py +3 -2
- examples/tutorial_sync.py +4 -3
- scripts/add_imports_to_inits.py +6 -2
- scripts/generate_functions_md.py +2 -1
- {architect_py-5.1.5.dist-info → architect_py-5.2.0.dist-info}/WHEEL +0 -0
- {architect_py-5.1.5.dist-info → architect_py-5.2.0.dist-info}/licenses/LICENSE +0 -0
- {architect_py-5.1.5.dist-info → architect_py-5.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,422 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
import uuid
|
4
|
+
from datetime import datetime
|
5
|
+
from decimal import Decimal
|
6
|
+
from typing import AsyncIterable, Dict, Optional, Sequence, Union
|
7
|
+
|
8
|
+
import grpc
|
9
|
+
import msgspec
|
10
|
+
|
11
|
+
from .grpc.client import dec_hook
|
12
|
+
from .grpc.models.Cpty.CptyRequest import (
|
13
|
+
CancelOrder,
|
14
|
+
Login,
|
15
|
+
Logout,
|
16
|
+
PlaceOrder,
|
17
|
+
UnannotatedCptyRequest,
|
18
|
+
)
|
19
|
+
from .grpc.models.Cpty.CptyResponse import (
|
20
|
+
ReconcileOpenOrders,
|
21
|
+
Symbology,
|
22
|
+
UpdateAccountSummary,
|
23
|
+
)
|
24
|
+
from .grpc.models.definitions import (
|
25
|
+
AccountIdOrName,
|
26
|
+
AccountPosition,
|
27
|
+
AccountStatistics,
|
28
|
+
CptyLoginRequest,
|
29
|
+
CptyLogoutRequest,
|
30
|
+
ExecutionInfo,
|
31
|
+
FillKind,
|
32
|
+
OrderDir,
|
33
|
+
OrderId,
|
34
|
+
OrderRejectReason,
|
35
|
+
UserId,
|
36
|
+
)
|
37
|
+
from .grpc.models.Oms.Cancel import Cancel
|
38
|
+
from .grpc.models.Oms.Order import Order
|
39
|
+
from .grpc.models.Orderflow.OrderflowRequest import (
|
40
|
+
OrderflowRequestUnannotatedResponseType,
|
41
|
+
TaggedCancelReject,
|
42
|
+
TaggedFill,
|
43
|
+
TaggedOrderAck,
|
44
|
+
TaggedOrderCanceled,
|
45
|
+
TaggedOrderOut,
|
46
|
+
TaggedOrderReject,
|
47
|
+
)
|
48
|
+
from .grpc.models.Orderflow.SubscribeOrderflowRequest import SubscribeOrderflowRequest
|
49
|
+
from .grpc.utils import encoder
|
50
|
+
|
51
|
+
FILLS_NS = uuid.UUID("c4b64693-40d2-5613-8d13-bf35b89f92e0")
|
52
|
+
|
53
|
+
|
54
|
+
class AsyncCpty:
|
55
|
+
"""
|
56
|
+
To implement an external cpty, subclass `AsyncCpty` and implement the callback
|
57
|
+
stubs `on_login`, `on_place_order`, etc. Use provided methods `ack_order`,
|
58
|
+
`out_order`, etc. to drive orderflow events to consumers.
|
59
|
+
|
60
|
+
Call `add_execution_info` to add execution info for a symbol.
|
61
|
+
|
62
|
+
Call `serve` to start the server and wait for termination.
|
63
|
+
|
64
|
+
For manual control of the grpc server, create your own `grpc.aio.Server`
|
65
|
+
and call `_add_method_handlers` to add the method handlers.
|
66
|
+
|
67
|
+
You can use as much or as little of this helper as desired.
|
68
|
+
"""
|
69
|
+
|
70
|
+
def __init__(self, execution_venue: str):
|
71
|
+
self.execution_venue = execution_venue
|
72
|
+
self.execution_venue_fills_ns = uuid.uuid5(FILLS_NS, execution_venue)
|
73
|
+
self.execution_info: Dict[str, Dict[str, ExecutionInfo]] = {}
|
74
|
+
self.cpty_notifications: dict[int, CptyNotifications] = {}
|
75
|
+
self.orderflow_subscriptions: dict[int, OrderflowSubscription] = {}
|
76
|
+
self.next_subscription_id = 1
|
77
|
+
|
78
|
+
def add_execution_info(self, symbol: str, execution_info: ExecutionInfo):
|
79
|
+
"""
|
80
|
+
Add execution info for a symbol.
|
81
|
+
"""
|
82
|
+
self.execution_info[self.execution_venue][symbol] = execution_info
|
83
|
+
|
84
|
+
async def on_login(self, _request: CptyLoginRequest):
|
85
|
+
raise NotImplementedError
|
86
|
+
|
87
|
+
async def on_logout(self, _request: CptyLogoutRequest):
|
88
|
+
raise NotImplementedError
|
89
|
+
|
90
|
+
async def on_place_order(self, _order: Order):
|
91
|
+
"""
|
92
|
+
Called when the cpty receives an order to place.
|
93
|
+
|
94
|
+
Call `ack_order` or `reject_order` to advance the order state;
|
95
|
+
otherwise, the order will be left in the `PENDING` state.
|
96
|
+
"""
|
97
|
+
raise NotImplementedError
|
98
|
+
|
99
|
+
async def on_cancel_order(
|
100
|
+
self, _cancel: Cancel, _original_order: Optional[Order] = None
|
101
|
+
):
|
102
|
+
"""
|
103
|
+
Called when the cpty receives a cancel order request.
|
104
|
+
|
105
|
+
Call `reject_cancel` if there's a problem with canceling the order;
|
106
|
+
make sure to reference the `cancel.cancel_id` to identify the cancel.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
cancel: The cancel order request.
|
110
|
+
original_order: The original order that was cancelled, if the OMS knows it.
|
111
|
+
"""
|
112
|
+
raise NotImplementedError
|
113
|
+
|
114
|
+
async def get_open_orders(self) -> Sequence[Order]:
|
115
|
+
"""
|
116
|
+
Get all open orders. This is called at least once per client
|
117
|
+
connection/login. Return a list of known open orders.
|
118
|
+
"""
|
119
|
+
return []
|
120
|
+
|
121
|
+
def ack_order(self, order_id: OrderId, *, exchange_order_id: Optional[str] = None):
|
122
|
+
"""
|
123
|
+
Acknowledge an order has reached the exchange.
|
124
|
+
|
125
|
+
Optionally, provide the exchange order id to correlate with the order;
|
126
|
+
this helps with order reconciliation.
|
127
|
+
"""
|
128
|
+
order_ack = TaggedOrderAck(order_id, exchange_order_id)
|
129
|
+
self._put_orderflow_event(order_ack)
|
130
|
+
|
131
|
+
def reject_order(
|
132
|
+
self,
|
133
|
+
order_id: OrderId,
|
134
|
+
*,
|
135
|
+
reject_reason: OrderRejectReason,
|
136
|
+
reject_message: Optional[str] = None,
|
137
|
+
):
|
138
|
+
"""
|
139
|
+
Reject an order.
|
140
|
+
"""
|
141
|
+
order_reject = TaggedOrderReject(order_id, reject_reason, reject_message)
|
142
|
+
self._put_orderflow_event(order_reject)
|
143
|
+
|
144
|
+
def out_order(self, order_id: OrderId, *, canceled: bool = False):
|
145
|
+
"""
|
146
|
+
Notify that an order is outed. If it was outed because of a cancel,
|
147
|
+
pass `canceled=True`. For all other reasons, pass `canceled=False`.
|
148
|
+
"""
|
149
|
+
if canceled:
|
150
|
+
order_out = TaggedOrderCanceled(order_id)
|
151
|
+
else:
|
152
|
+
order_out = TaggedOrderOut(order_id)
|
153
|
+
self._put_orderflow_event(order_out)
|
154
|
+
|
155
|
+
def fill_order(
|
156
|
+
self,
|
157
|
+
*,
|
158
|
+
dir: OrderDir,
|
159
|
+
exchange_fill_id: str,
|
160
|
+
# if not provided, a suitable uuiv5 will be generated from the exchange_fill_id
|
161
|
+
fill_id: Optional[str] = None,
|
162
|
+
fill_kind: FillKind = FillKind.Normal,
|
163
|
+
price: Decimal,
|
164
|
+
quantity: Decimal,
|
165
|
+
symbol: str,
|
166
|
+
trade_time: datetime,
|
167
|
+
account: AccountIdOrName,
|
168
|
+
is_taker: bool,
|
169
|
+
fee: Optional[Decimal] = None,
|
170
|
+
fee_currency: Optional[str] = None,
|
171
|
+
order_id: OrderId,
|
172
|
+
trader: Optional[UserId] = None,
|
173
|
+
):
|
174
|
+
"""
|
175
|
+
Notify that an order has been filled, either partially or in full.
|
176
|
+
"""
|
177
|
+
now = datetime.now()
|
178
|
+
if fill_id is None:
|
179
|
+
fill_id = str(uuid.uuid5(self.execution_venue_fills_ns, exchange_fill_id))
|
180
|
+
self._put_orderflow_event(
|
181
|
+
TaggedFill(
|
182
|
+
dir,
|
183
|
+
fill_id,
|
184
|
+
fill_kind,
|
185
|
+
price,
|
186
|
+
quantity,
|
187
|
+
symbol,
|
188
|
+
int(trade_time.timestamp()),
|
189
|
+
trade_time.microsecond * 1000,
|
190
|
+
self.execution_venue,
|
191
|
+
account,
|
192
|
+
1 if is_taker else 0,
|
193
|
+
int(now.timestamp()),
|
194
|
+
now.microsecond * 1000,
|
195
|
+
fee,
|
196
|
+
fee_currency,
|
197
|
+
order_id,
|
198
|
+
trader,
|
199
|
+
exchange_fill_id,
|
200
|
+
)
|
201
|
+
)
|
202
|
+
|
203
|
+
def reject_cancel(
|
204
|
+
self,
|
205
|
+
cancel_id: str,
|
206
|
+
*,
|
207
|
+
reject_reason: str,
|
208
|
+
reject_message: Optional[str] = None,
|
209
|
+
):
|
210
|
+
"""
|
211
|
+
Reject a cancel.
|
212
|
+
"""
|
213
|
+
self._put_orderflow_event(
|
214
|
+
TaggedCancelReject(cancel_id, reject_reason, reject_message)
|
215
|
+
)
|
216
|
+
|
217
|
+
def _put_orderflow_event(self, event):
|
218
|
+
for sub_id, sub in self.orderflow_subscriptions.items():
|
219
|
+
try:
|
220
|
+
sub.queue.put_nowait(event)
|
221
|
+
except asyncio.QueueFull:
|
222
|
+
logging.warn(f"orderflow subscription queue full #{sub_id}")
|
223
|
+
|
224
|
+
def update_account_summary(
|
225
|
+
self,
|
226
|
+
account: AccountIdOrName,
|
227
|
+
*,
|
228
|
+
is_snapshot: bool,
|
229
|
+
timestamp: datetime,
|
230
|
+
balances: Optional[Dict[str, Decimal]] = None,
|
231
|
+
positions: Optional[Dict[str, AccountPosition]] = None,
|
232
|
+
cash_excess: Optional[Decimal] = None,
|
233
|
+
equity: Optional[Decimal] = None,
|
234
|
+
yesterday_equity: Optional[Decimal] = None,
|
235
|
+
position_margin: Optional[Decimal] = None,
|
236
|
+
purchasing_power: Optional[Decimal] = None,
|
237
|
+
realized_pnl: Optional[Decimal] = None,
|
238
|
+
unrealized_pnl: Optional[Decimal] = None,
|
239
|
+
):
|
240
|
+
"""
|
241
|
+
Update account summary, as reported by the exchange.
|
242
|
+
|
243
|
+
Not all fields are required--fill only the fields that are relevant.
|
244
|
+
"""
|
245
|
+
positions_dict = None
|
246
|
+
if positions is not None:
|
247
|
+
positions_dict = {}
|
248
|
+
for symbol, position in positions.items():
|
249
|
+
positions_dict[symbol] = [position]
|
250
|
+
self._put_cpty_event(
|
251
|
+
UpdateAccountSummary(
|
252
|
+
account,
|
253
|
+
is_snapshot,
|
254
|
+
int(timestamp.timestamp()),
|
255
|
+
timestamp.microsecond * 1000,
|
256
|
+
balances,
|
257
|
+
positions_dict,
|
258
|
+
AccountStatistics(
|
259
|
+
cash_excess=cash_excess,
|
260
|
+
equity=equity,
|
261
|
+
yesterday_equity=yesterday_equity,
|
262
|
+
position_margin=position_margin,
|
263
|
+
purchasing_power=purchasing_power,
|
264
|
+
realized_pnl=realized_pnl,
|
265
|
+
unrealized_pnl=unrealized_pnl,
|
266
|
+
),
|
267
|
+
)
|
268
|
+
)
|
269
|
+
|
270
|
+
def _put_cpty_event(self, event):
|
271
|
+
for sub_id, sub in self.cpty_notifications.items():
|
272
|
+
try:
|
273
|
+
sub.queue.put_nowait(event)
|
274
|
+
except asyncio.QueueFull:
|
275
|
+
logging.warn(f"cpty notification queue full #{sub_id}")
|
276
|
+
|
277
|
+
async def Cpty(
|
278
|
+
self,
|
279
|
+
request_iterator: AsyncIterable[UnannotatedCptyRequest],
|
280
|
+
context: grpc.aio.ServicerContext,
|
281
|
+
):
|
282
|
+
context.set_code(grpc.StatusCode.OK)
|
283
|
+
await context.send_initial_metadata([])
|
284
|
+
logged_in: Optional[Login] = None
|
285
|
+
subscription_id = self.next_subscription_id
|
286
|
+
self.next_subscription_id += 1
|
287
|
+
|
288
|
+
def cleanup_subscription(_context):
|
289
|
+
if subscription_id is not None:
|
290
|
+
try:
|
291
|
+
del self.cpty_notifications[subscription_id]
|
292
|
+
logging.debug(f"cleaned up cpty notification #{subscription_id}")
|
293
|
+
except KeyError:
|
294
|
+
pass
|
295
|
+
|
296
|
+
context.add_done_callback(cleanup_subscription)
|
297
|
+
async for request in request_iterator:
|
298
|
+
logging.debug(f"Cpty: {request}")
|
299
|
+
if not logged_in and not isinstance(request, Login):
|
300
|
+
logging.error("not logged in, skipping request")
|
301
|
+
continue
|
302
|
+
if isinstance(request, Login):
|
303
|
+
try:
|
304
|
+
await self.on_login(request)
|
305
|
+
logged_in = request
|
306
|
+
logging.debug(f"registered cpty notification #{subscription_id}")
|
307
|
+
self.cpty_notifications[subscription_id] = CptyNotifications(
|
308
|
+
account=request.account,
|
309
|
+
trader=request.trader,
|
310
|
+
)
|
311
|
+
except NotImplementedError:
|
312
|
+
logging.error("on_login not implemented")
|
313
|
+
if logged_in:
|
314
|
+
# send symbology info to client
|
315
|
+
yield Symbology(self.execution_info)
|
316
|
+
# send open orders to client
|
317
|
+
open_orders = await self.get_open_orders()
|
318
|
+
yield ReconcileOpenOrders(list(open_orders))
|
319
|
+
elif isinstance(request, Logout):
|
320
|
+
try:
|
321
|
+
del self.cpty_notifications[subscription_id]
|
322
|
+
logged_in = None
|
323
|
+
await self.on_logout(request)
|
324
|
+
except NotImplementedError:
|
325
|
+
logging.error("on_logout not implemented")
|
326
|
+
elif isinstance(request, PlaceOrder):
|
327
|
+
try:
|
328
|
+
await self.on_place_order(request)
|
329
|
+
except NotImplementedError:
|
330
|
+
logging.error("on_place_order not implemented")
|
331
|
+
elif isinstance(request, CancelOrder):
|
332
|
+
try:
|
333
|
+
await self.on_cancel_order(request.cancel, request.original_order)
|
334
|
+
except NotImplementedError:
|
335
|
+
logging.error("on_cancel_order not implemented")
|
336
|
+
else:
|
337
|
+
logging.error(f"unhandled cpty request: {request}")
|
338
|
+
|
339
|
+
async def SubscribeOrderflow(
|
340
|
+
self, request: SubscribeOrderflowRequest, context: grpc.aio.ServicerContext
|
341
|
+
):
|
342
|
+
context.set_code(grpc.StatusCode.OK)
|
343
|
+
await context.send_initial_metadata([])
|
344
|
+
logging.debug(f"Orderflow: {request}")
|
345
|
+
subscription_id = self.next_subscription_id
|
346
|
+
self.next_subscription_id += 1
|
347
|
+
logging.debug(f"registered orderflow subscription #{subscription_id}")
|
348
|
+
|
349
|
+
def cleanup_subscription(_context):
|
350
|
+
del self.orderflow_subscriptions[subscription_id]
|
351
|
+
logging.debug(f"cleaned up orderflow subscription #{subscription_id}")
|
352
|
+
|
353
|
+
context.add_done_callback(cleanup_subscription)
|
354
|
+
subscription = OrderflowSubscription(request)
|
355
|
+
self.orderflow_subscriptions[subscription_id] = subscription
|
356
|
+
while True:
|
357
|
+
next_item = await subscription.queue.get()
|
358
|
+
yield next_item
|
359
|
+
|
360
|
+
def _add_cpty_method_handlers(self, server: grpc.aio.Server):
|
361
|
+
decoder = msgspec.json.Decoder(type=UnannotatedCptyRequest, dec_hook=dec_hook)
|
362
|
+
rpc_method_handlers = {
|
363
|
+
"Cpty": grpc.stream_stream_rpc_method_handler(
|
364
|
+
self.Cpty,
|
365
|
+
request_deserializer=decoder.decode,
|
366
|
+
response_serializer=encoder.encode,
|
367
|
+
),
|
368
|
+
}
|
369
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
370
|
+
"json.architect.Cpty", rpc_method_handlers
|
371
|
+
)
|
372
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
373
|
+
|
374
|
+
def _add_orderflow_method_handlers(self, server: grpc.aio.Server):
|
375
|
+
decoder = msgspec.json.Decoder(
|
376
|
+
type=SubscribeOrderflowRequest, dec_hook=dec_hook
|
377
|
+
)
|
378
|
+
rpc_method_handlers = {
|
379
|
+
"SubscribeOrderflow": grpc.unary_stream_rpc_method_handler(
|
380
|
+
self.SubscribeOrderflow,
|
381
|
+
request_deserializer=decoder.decode,
|
382
|
+
response_serializer=encoder.encode,
|
383
|
+
),
|
384
|
+
}
|
385
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
386
|
+
"json.architect.Orderflow", rpc_method_handlers
|
387
|
+
)
|
388
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
389
|
+
|
390
|
+
def _add_method_handlers(self, server: grpc.aio.Server):
|
391
|
+
self._add_cpty_method_handlers(server)
|
392
|
+
self._add_orderflow_method_handlers(server)
|
393
|
+
|
394
|
+
async def serve(self, bind: str):
|
395
|
+
server = grpc.aio.server()
|
396
|
+
self._add_method_handlers(server)
|
397
|
+
server.add_insecure_port(bind)
|
398
|
+
await server.start()
|
399
|
+
logging.info(f"grpc server started on {bind}")
|
400
|
+
await server.wait_for_termination()
|
401
|
+
|
402
|
+
|
403
|
+
class CptyNotifications:
|
404
|
+
account: Optional[AccountIdOrName] = None
|
405
|
+
trader: Optional[str] = None
|
406
|
+
queue: asyncio.Queue[Union[Symbology, ReconcileOpenOrders, UpdateAccountSummary]]
|
407
|
+
|
408
|
+
def __init__(
|
409
|
+
self, account: Optional[AccountIdOrName] = None, trader: Optional[str] = None
|
410
|
+
):
|
411
|
+
self.account = account
|
412
|
+
self.trader = trader
|
413
|
+
self.queue = asyncio.Queue()
|
414
|
+
|
415
|
+
|
416
|
+
class OrderflowSubscription:
|
417
|
+
request: SubscribeOrderflowRequest
|
418
|
+
queue: asyncio.Queue[OrderflowRequestUnannotatedResponseType]
|
419
|
+
|
420
|
+
def __init__(self, request: SubscribeOrderflowRequest):
|
421
|
+
self.request = request
|
422
|
+
self.queue = asyncio.Queue()
|
architect_py/client.pyi
CHANGED
@@ -75,45 +75,14 @@ class Client:
|
|
75
75
|
|
76
76
|
Query methods on Client that require auth will call this method internally.
|
77
77
|
"""
|
78
|
-
def set_jwt(self, jwt: str | None, jwt_expiration: datetime | None = None):
|
79
|
-
"""
|
80
|
-
Manually set the JWT for gRPC authentication.
|
81
|
-
|
82
|
-
Args:
|
83
|
-
jwt: the JWT to set;
|
84
|
-
None to clear the JWT
|
85
|
-
jwt_expiration: when to expire the JWT
|
86
|
-
"""
|
87
|
-
def discover_marketdata(self) -> None:
|
88
|
-
"""
|
89
|
-
Load marketdata endpoints from the server config.
|
90
|
-
|
91
|
-
The Architect core is responsible for telling you where to find marketdata as per
|
92
|
-
its configuration. You can also manually set marketdata endpoints by calling
|
93
|
-
set_marketdata directly.
|
94
|
-
|
95
|
-
This method is called on Client.connect.
|
96
|
-
"""
|
97
78
|
def set_marketdata(self, venue: Venue, endpoint: str):
|
98
79
|
"""
|
99
80
|
Manually set the marketdata endpoint for a venue.
|
100
81
|
"""
|
101
|
-
def marketdata(self, venue: Venue) -> GrpcClient:
|
102
|
-
"""
|
103
|
-
Get the marketdata client for a venue.
|
104
|
-
"""
|
105
82
|
def set_hmart(self, endpoint: str):
|
106
83
|
"""
|
107
84
|
Manually set the hmart (historical marketdata service) endpoint.
|
108
85
|
"""
|
109
|
-
def hmart(self) -> GrpcClient:
|
110
|
-
"""
|
111
|
-
Get the hmart (historical marketdata service) client.
|
112
|
-
"""
|
113
|
-
def core(self) -> GrpcClient:
|
114
|
-
"""
|
115
|
-
Get the core client.
|
116
|
-
"""
|
117
86
|
def who_am_i(self) -> tuple[str, str]:
|
118
87
|
"""
|
119
88
|
Gets the user_id and user_email for the user that the API key belongs to.
|
@@ -121,7 +90,6 @@ class Client:
|
|
121
90
|
Returns:
|
122
91
|
(user_id, user_email)
|
123
92
|
"""
|
124
|
-
def auth_info(self) -> AuthInfoResponse: ...
|
125
93
|
def cpty_status(self, kind: str, instance: str | None = None) -> CptyStatus:
|
126
94
|
"""
|
127
95
|
Get cpty status.
|
@@ -509,7 +477,7 @@ class Client:
|
|
509
477
|
symbol: the symbol to send the order for
|
510
478
|
execution_venue: the execution venue to send the order to,
|
511
479
|
if execution_venue is set to None, the OMS will send the order to the primary_exchange
|
512
|
-
the primary_exchange can be deduced from `get_product_info`
|
480
|
+
the primary_exchange can be deduced from `get_product_info` (generally will be "CME" or "US-EQUITIES")
|
513
481
|
dir: the direction of the order, BUY or SELL
|
514
482
|
quantity: the quantity of the order
|
515
483
|
limit_price: the limit price of the order
|
@@ -569,7 +537,7 @@ class Client:
|
|
569
537
|
True if all orders were cancelled successfully
|
570
538
|
False if there was an error
|
571
539
|
"""
|
572
|
-
def
|
540
|
+
def place_algo_order(self, *, params: SpreaderParams, id: str | None = None, trader: str | None = None):
|
573
541
|
"""
|
574
542
|
Sends an advanced algo order such as the spreader.
|
575
543
|
"""
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# generated by datamodel-codegen:
|
2
|
+
# filename: Accounts/ResetPaperAccountRequest.json
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
from architect_py.grpc.models.Accounts.ResetPaperAccountResponse import (
|
6
|
+
ResetPaperAccountResponse,
|
7
|
+
)
|
8
|
+
|
9
|
+
from typing import Annotated, Optional
|
10
|
+
|
11
|
+
from msgspec import Meta, Struct
|
12
|
+
|
13
|
+
from .. import definitions
|
14
|
+
|
15
|
+
|
16
|
+
class ResetPaperAccountRequest(Struct, omit_defaults=True):
|
17
|
+
account: Annotated[
|
18
|
+
definitions.AccountIdOrName,
|
19
|
+
Meta(
|
20
|
+
description="The trader for whom to reset paper accounts. If not specified, defaults to the caller user."
|
21
|
+
),
|
22
|
+
]
|
23
|
+
"""
|
24
|
+
The trader for whom to reset paper accounts. If not specified, defaults to the caller user.
|
25
|
+
"""
|
26
|
+
balance: Optional[int] = None
|
27
|
+
|
28
|
+
# Constructor that takes all field titles as arguments for convenience
|
29
|
+
@classmethod
|
30
|
+
def new(
|
31
|
+
cls,
|
32
|
+
account: definitions.AccountIdOrName,
|
33
|
+
balance: Optional[int] = None,
|
34
|
+
):
|
35
|
+
return cls(
|
36
|
+
account,
|
37
|
+
balance,
|
38
|
+
)
|
39
|
+
|
40
|
+
def __str__(self) -> str:
|
41
|
+
return (
|
42
|
+
f"ResetPaperAccountRequest(account={self.account},balance={self.balance})"
|
43
|
+
)
|
44
|
+
|
45
|
+
@staticmethod
|
46
|
+
def get_response_type():
|
47
|
+
return ResetPaperAccountResponse
|
48
|
+
|
49
|
+
@staticmethod
|
50
|
+
def get_unannotated_response_type():
|
51
|
+
return ResetPaperAccountResponse
|
52
|
+
|
53
|
+
@staticmethod
|
54
|
+
def get_route() -> str:
|
55
|
+
return "/json.architect.Accounts/ResetPaperAccount"
|
56
|
+
|
57
|
+
@staticmethod
|
58
|
+
def get_rpc_method():
|
59
|
+
return "unary"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# generated by datamodel-codegen:
|
2
|
+
# filename: Accounts/ResetPaperAccountResponse.json
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
from msgspec import Struct
|
7
|
+
|
8
|
+
|
9
|
+
class ResetPaperAccountResponse(Struct, omit_defaults=True):
|
10
|
+
pass
|
11
|
+
|
12
|
+
# Constructor that takes all field titles as arguments for convenience
|
13
|
+
@classmethod
|
14
|
+
def new(
|
15
|
+
cls,
|
16
|
+
):
|
17
|
+
return cls()
|
18
|
+
|
19
|
+
def __str__(self) -> str:
|
20
|
+
return f"ResetPaperAccountResponse()"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# generated by datamodel-codegen:
|
2
|
+
# filename: Boss/OptionsTransactionsRequest.json
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
from architect_py.grpc.models.Boss.OptionsTransactionsResponse import (
|
6
|
+
OptionsTransactionsResponse,
|
7
|
+
)
|
8
|
+
|
9
|
+
from msgspec import Struct
|
10
|
+
|
11
|
+
|
12
|
+
class OptionsTransactionsRequest(Struct, omit_defaults=True):
|
13
|
+
account_id: str
|
14
|
+
|
15
|
+
# Constructor that takes all field titles as arguments for convenience
|
16
|
+
@classmethod
|
17
|
+
def new(
|
18
|
+
cls,
|
19
|
+
account_id: str,
|
20
|
+
):
|
21
|
+
return cls(
|
22
|
+
account_id,
|
23
|
+
)
|
24
|
+
|
25
|
+
def __str__(self) -> str:
|
26
|
+
return f"OptionsTransactionsRequest(account_id={self.account_id})"
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def get_response_type():
|
30
|
+
return OptionsTransactionsResponse
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
def get_unannotated_response_type():
|
34
|
+
return OptionsTransactionsResponse
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def get_route() -> str:
|
38
|
+
return "/json.architect.Boss/OptionsTransactions"
|
39
|
+
|
40
|
+
@staticmethod
|
41
|
+
def get_rpc_method():
|
42
|
+
return "unary"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# generated by datamodel-codegen:
|
2
|
+
# filename: Boss/OptionsTransactionsResponse.json
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
from typing import List
|
7
|
+
|
8
|
+
from msgspec import Struct
|
9
|
+
|
10
|
+
from .. import definitions
|
11
|
+
|
12
|
+
|
13
|
+
class OptionsTransactionsResponse(Struct, omit_defaults=True):
|
14
|
+
options_transactions: List[definitions.OptionsTransaction]
|
15
|
+
|
16
|
+
# Constructor that takes all field titles as arguments for convenience
|
17
|
+
@classmethod
|
18
|
+
def new(
|
19
|
+
cls,
|
20
|
+
options_transactions: List[definitions.OptionsTransaction],
|
21
|
+
):
|
22
|
+
return cls(
|
23
|
+
options_transactions,
|
24
|
+
)
|
25
|
+
|
26
|
+
def __str__(self) -> str:
|
27
|
+
return f"OptionsTransactionsResponse(options_transactions={self.options_transactions})"
|
@@ -7,19 +7,19 @@ from typing import List
|
|
7
7
|
|
8
8
|
from msgspec import Struct
|
9
9
|
|
10
|
-
from
|
10
|
+
from .OptionsContract import OptionsContract
|
11
11
|
|
12
12
|
|
13
13
|
class OptionsChain(Struct, omit_defaults=True):
|
14
|
-
calls: List[
|
15
|
-
puts: List[
|
14
|
+
calls: List[OptionsContract]
|
15
|
+
puts: List[OptionsContract]
|
16
16
|
|
17
17
|
# Constructor that takes all field titles as arguments for convenience
|
18
18
|
@classmethod
|
19
19
|
def new(
|
20
20
|
cls,
|
21
|
-
calls: List[
|
22
|
-
puts: List[
|
21
|
+
calls: List[OptionsContract],
|
22
|
+
puts: List[OptionsContract],
|
23
23
|
):
|
24
24
|
return cls(
|
25
25
|
calls,
|
@@ -7,19 +7,19 @@ from typing import List
|
|
7
7
|
|
8
8
|
from msgspec import Struct
|
9
9
|
|
10
|
-
from
|
10
|
+
from .OptionsGreeks import OptionsGreeks
|
11
11
|
|
12
12
|
|
13
13
|
class OptionsChainGreeks(Struct, omit_defaults=True):
|
14
|
-
calls: List[
|
15
|
-
puts: List[
|
14
|
+
calls: List[OptionsGreeks]
|
15
|
+
puts: List[OptionsGreeks]
|
16
16
|
|
17
17
|
# Constructor that takes all field titles as arguments for convenience
|
18
18
|
@classmethod
|
19
19
|
def new(
|
20
20
|
cls,
|
21
|
-
calls: List[
|
22
|
-
puts: List[
|
21
|
+
calls: List[OptionsGreeks],
|
22
|
+
puts: List[OptionsGreeks],
|
23
23
|
):
|
24
24
|
return cls(
|
25
25
|
calls,
|