architect-py 5.1.4b1__py3-none-any.whl → 5.1.5__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 +19 -1
- architect_py/async_client.py +158 -45
- architect_py/client.py +4 -0
- architect_py/client.pyi +33 -9
- architect_py/grpc/client.py +24 -9
- architect_py/grpc/models/AlgoHelper/AlgoParamTypes.py +31 -0
- architect_py/grpc/models/AlgoHelper/__init__.py +2 -0
- architect_py/grpc/models/Auth/AuthInfoRequest.py +37 -0
- architect_py/grpc/models/Auth/AuthInfoResponse.py +30 -0
- architect_py/grpc/models/Core/ConfigResponse.py +7 -2
- architect_py/grpc/models/Folio/AccountHistoryRequest.py +35 -4
- architect_py/grpc/models/Folio/AccountSummary.py +7 -1
- architect_py/grpc/models/Marketdata/Candle.py +6 -0
- architect_py/grpc/models/Marketdata/L1BookSnapshot.py +17 -3
- architect_py/grpc/models/Marketdata/L2BookSnapshot.py +6 -0
- architect_py/grpc/models/Marketdata/Liquidation.py +6 -0
- architect_py/grpc/models/Marketdata/Ticker.py +6 -0
- architect_py/grpc/models/Marketdata/Trade.py +6 -0
- architect_py/grpc/models/Oms/Order.py +38 -14
- architect_py/grpc/models/Oms/PlaceOrderRequest.py +38 -14
- architect_py/grpc/models/__init__.py +4 -1
- architect_py/grpc/models/definitions.py +173 -0
- architect_py/grpc/orderflow.py +138 -0
- architect_py/tests/test_order_entry.py +9 -6
- architect_py/tests/test_orderflow.py +116 -27
- architect_py/tests/test_positions.py +173 -0
- architect_py/tests/test_rounding.py +28 -28
- architect_py/utils/pandas.py +50 -1
- {architect_py-5.1.4b1.dist-info → architect_py-5.1.5.dist-info}/METADATA +1 -1
- {architect_py-5.1.4b1.dist-info → architect_py-5.1.5.dist-info}/RECORD +35 -29
- examples/funding_rate_mean_reversion_algo.py +23 -47
- scripts/postprocess_grpc.py +17 -2
- {architect_py-5.1.4b1.dist-info → architect_py-5.1.5.dist-info}/WHEEL +0 -0
- {architect_py-5.1.4b1.dist-info → architect_py-5.1.5.dist-info}/licenses/LICENSE +0 -0
- {architect_py-5.1.4b1.dist-info → architect_py-5.1.5.dist-info}/top_level.txt +0 -0
architect_py/__init__.py
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# ruff: noqa:I001
|
2
2
|
|
3
|
-
__version__ = "5.1.
|
3
|
+
__version__ = "5.1.5"
|
4
4
|
|
5
5
|
from .utils.nearest_tick import TickRoundMethod
|
6
6
|
from .async_client import AsyncClient
|
7
7
|
from .client import Client
|
8
8
|
from .common_types import OrderDir, TradableProduct, TimeInForce, Venue
|
9
9
|
from .grpc.models.definitions import (
|
10
|
+
AccountHistoryGranularity,
|
10
11
|
AccountIdOrName,
|
11
12
|
AccountPosition,
|
12
13
|
AccountStatistics,
|
@@ -32,12 +33,14 @@ from .grpc.models.definitions import (
|
|
32
33
|
SortTickersBy,
|
33
34
|
Statement,
|
34
35
|
TraderIdOrEmail,
|
36
|
+
TriggerLimitOrderType,
|
35
37
|
UserId,
|
36
38
|
Withdrawal,
|
37
39
|
AccountPermissions,
|
38
40
|
AliasKind,
|
39
41
|
DerivativeKind,
|
40
42
|
FillKind,
|
43
|
+
HumanDuration,
|
41
44
|
Unit,
|
42
45
|
MinOrderQuantityUnit,
|
43
46
|
OptionsExerciseType,
|
@@ -55,6 +58,7 @@ from .grpc.models.definitions import (
|
|
55
58
|
SnapshotOrUpdateForStringAndProductCatalogInfo2,
|
56
59
|
SnapshotOrUpdateForStringAndString1,
|
57
60
|
SnapshotOrUpdateForStringAndString2,
|
61
|
+
SpreaderPhase,
|
58
62
|
SimpleDecimal,
|
59
63
|
Varying1,
|
60
64
|
Varying,
|
@@ -82,6 +86,8 @@ from .grpc.models.definitions import (
|
|
82
86
|
SnapshotOrUpdateForStringAndOptionsSeriesInfo2,
|
83
87
|
SnapshotOrUpdateForStringAndSnapshotOrUpdateForStringAndProductCatalogInfo1,
|
84
88
|
SnapshotOrUpdateForStringAndSnapshotOrUpdateForStringAndProductCatalogInfo2,
|
89
|
+
SpreaderParams,
|
90
|
+
SpreaderStatus,
|
85
91
|
Account,
|
86
92
|
FutureSpread,
|
87
93
|
Option,
|
@@ -110,6 +116,9 @@ from .grpc.models.Algo.StartAlgoRequest import StartAlgoRequest
|
|
110
116
|
from .grpc.models.Algo.StartAlgoResponse import StartAlgoResponse
|
111
117
|
from .grpc.models.Algo.StopAlgoRequest import StopAlgoRequest
|
112
118
|
from .grpc.models.Algo.StopAlgoResponse import StopAlgoResponse
|
119
|
+
from .grpc.models.AlgoHelper.AlgoParamTypes import AlgoParamTypes
|
120
|
+
from .grpc.models.Auth.AuthInfoRequest import AuthInfoRequest
|
121
|
+
from .grpc.models.Auth.AuthInfoResponse import AuthInfoResponse
|
113
122
|
from .grpc.models.Auth.CreateJwtRequest import CreateJwtRequest
|
114
123
|
from .grpc.models.Auth.CreateJwtResponse import CreateJwtResponse
|
115
124
|
from .grpc.models.Boss.DepositsRequest import DepositsRequest
|
@@ -236,6 +245,7 @@ from .grpc.models.Symbology.UploadSymbologyResponse import UploadSymbologyRespon
|
|
236
245
|
__all__ = [
|
237
246
|
"AberrantFill",
|
238
247
|
"Account",
|
248
|
+
"AccountHistoryGranularity",
|
239
249
|
"AccountHistoryRequest",
|
240
250
|
"AccountHistoryResponse",
|
241
251
|
"AccountIdOrName",
|
@@ -255,9 +265,12 @@ __all__ = [
|
|
255
265
|
"AlgoOrderStatus",
|
256
266
|
"AlgoOrdersRequest",
|
257
267
|
"AlgoOrdersResponse",
|
268
|
+
"AlgoParamTypes",
|
258
269
|
"AliasKind",
|
259
270
|
"ArrayOfL1BookSnapshot",
|
260
271
|
"AsyncClient",
|
272
|
+
"AuthInfoRequest",
|
273
|
+
"AuthInfoResponse",
|
261
274
|
"Cancel",
|
262
275
|
"CancelAllOrdersRequest",
|
263
276
|
"CancelAllOrdersResponse",
|
@@ -315,6 +328,7 @@ __all__ = [
|
|
315
328
|
"HistoricalFillsResponse",
|
316
329
|
"HistoricalOrdersRequest",
|
317
330
|
"HistoricalOrdersResponse",
|
331
|
+
"HumanDuration",
|
318
332
|
"Index",
|
319
333
|
"L1BookSnapshot",
|
320
334
|
"L1BookSnapshotRequest",
|
@@ -394,6 +408,9 @@ __all__ = [
|
|
394
408
|
"SnapshotOrUpdateForStringAndString2",
|
395
409
|
"SortTickersBy",
|
396
410
|
"SpreadLeg",
|
411
|
+
"SpreaderParams",
|
412
|
+
"SpreaderPhase",
|
413
|
+
"SpreaderStatus",
|
397
414
|
"StartAlgoRequest",
|
398
415
|
"StartAlgoResponse",
|
399
416
|
"Statement",
|
@@ -428,6 +445,7 @@ __all__ = [
|
|
428
445
|
"TradableProduct",
|
429
446
|
"Trade",
|
430
447
|
"TraderIdOrEmail",
|
448
|
+
"TriggerLimitOrderType",
|
431
449
|
"Unit",
|
432
450
|
"Unknown",
|
433
451
|
"UploadProductCatalogRequest",
|
architect_py/async_client.py
CHANGED
@@ -6,7 +6,6 @@ from decimal import Decimal
|
|
6
6
|
from typing import (
|
7
7
|
Any,
|
8
8
|
AsyncGenerator,
|
9
|
-
AsyncIterator,
|
10
9
|
List,
|
11
10
|
Literal,
|
12
11
|
Optional,
|
@@ -16,7 +15,6 @@ from typing import (
|
|
16
15
|
overload,
|
17
16
|
)
|
18
17
|
|
19
|
-
import grpc
|
20
18
|
import pandas as pd
|
21
19
|
|
22
20
|
# cannot do architect_py import * due to circular import
|
@@ -38,16 +36,15 @@ from architect_py.grpc.models.definitions import (
|
|
38
36
|
OrderSource,
|
39
37
|
OrderType,
|
40
38
|
SortTickersBy,
|
39
|
+
SpreaderParams,
|
41
40
|
TraderIdOrEmail,
|
41
|
+
TriggerLimitOrderType,
|
42
42
|
)
|
43
|
-
from architect_py.grpc.
|
44
|
-
OrderflowRequest_route,
|
45
|
-
OrderflowRequestUnannotatedResponseType,
|
46
|
-
)
|
43
|
+
from architect_py.grpc.orderflow import OrderflowChannel
|
47
44
|
from architect_py.grpc.resolve_endpoint import PAPER_GRPC_PORT, resolve_endpoint
|
48
45
|
from architect_py.utils.nearest_tick import TickRoundMethod
|
49
46
|
from architect_py.utils.orderbook import update_orderbook_side
|
50
|
-
from architect_py.utils.pandas import candles_to_dataframe
|
47
|
+
from architect_py.utils.pandas import candles_to_dataframe, tickers_to_dataframe
|
51
48
|
from architect_py.utils.price_bands import price_band_pairs
|
52
49
|
from architect_py.utils.symbol_parsing import nominative_expiration
|
53
50
|
|
@@ -56,6 +53,8 @@ class AsyncClient:
|
|
56
53
|
api_key: Optional[str] = None
|
57
54
|
api_secret: Optional[str] = None
|
58
55
|
paper_trading: bool
|
56
|
+
as_user: Optional[str] = None
|
57
|
+
as_role: Optional[str] = None
|
59
58
|
graphql_client: GraphQLClient
|
60
59
|
grpc_options: Sequence[Tuple[str, Any]] | None = None
|
61
60
|
grpc_core: Optional[GrpcClient] = None
|
@@ -80,6 +79,8 @@ class AsyncClient:
|
|
80
79
|
endpoint: str = "https://app.architect.co",
|
81
80
|
graphql_port: Optional[int] = None,
|
82
81
|
grpc_options: Sequence[Tuple[str, Any]] | None = None,
|
82
|
+
as_user: Optional[str] = None,
|
83
|
+
as_role: Optional[str] = None,
|
83
84
|
**kwargs: Any,
|
84
85
|
) -> "AsyncClient":
|
85
86
|
"""
|
@@ -131,6 +132,8 @@ class AsyncClient:
|
|
131
132
|
api_key=api_key,
|
132
133
|
api_secret=api_secret,
|
133
134
|
paper_trading=paper_trading,
|
135
|
+
as_user=as_user,
|
136
|
+
as_role=as_role,
|
134
137
|
grpc_host=grpc_host,
|
135
138
|
grpc_port=grpc_port,
|
136
139
|
grpc_options=grpc_options,
|
@@ -153,6 +156,8 @@ class AsyncClient:
|
|
153
156
|
api_key: Optional[str] = None,
|
154
157
|
api_secret: Optional[str] = None,
|
155
158
|
paper_trading: bool,
|
159
|
+
as_user: Optional[str] = None,
|
160
|
+
as_role: Optional[str] = None,
|
156
161
|
grpc_host: str = "app.architect.co",
|
157
162
|
grpc_port: int,
|
158
163
|
grpc_options: Sequence[Tuple[str, Any]] | None = None,
|
@@ -184,6 +189,8 @@ class AsyncClient:
|
|
184
189
|
self.api_key = api_key
|
185
190
|
self.api_secret = api_secret
|
186
191
|
self.paper_trading = paper_trading
|
192
|
+
self.as_user = as_user
|
193
|
+
self.as_role = as_role
|
187
194
|
self.graphql_client = GraphQLClient(
|
188
195
|
host=grpc_host,
|
189
196
|
port=graphql_port,
|
@@ -193,7 +200,12 @@ class AsyncClient:
|
|
193
200
|
)
|
194
201
|
self.grpc_options = grpc_options
|
195
202
|
self.grpc_core = GrpcClient(
|
196
|
-
host=grpc_host,
|
203
|
+
host=grpc_host,
|
204
|
+
port=grpc_port,
|
205
|
+
use_ssl=use_ssl,
|
206
|
+
options=grpc_options,
|
207
|
+
as_user=as_user,
|
208
|
+
as_role=as_role,
|
197
209
|
)
|
198
210
|
|
199
211
|
async def close(self):
|
@@ -238,7 +250,9 @@ class AsyncClient:
|
|
238
250
|
):
|
239
251
|
try:
|
240
252
|
req = CreateJwtRequest(api_key=self.api_key, api_secret=self.api_secret)
|
241
|
-
res: CreateJwtResponse = await self.grpc_core.unary_unary(
|
253
|
+
res: CreateJwtResponse = await self.grpc_core.unary_unary(
|
254
|
+
req, no_metadata=True
|
255
|
+
)
|
242
256
|
self.jwt = res.jwt
|
243
257
|
# CR alee: actually read the JWT to get the expiration time;
|
244
258
|
# for now, we just "know" that the JWTs are granted for an hour
|
@@ -287,6 +301,8 @@ class AsyncClient:
|
|
287
301
|
port=grpc_port,
|
288
302
|
use_ssl=use_ssl,
|
289
303
|
options=self.grpc_options,
|
304
|
+
as_user=self.as_user,
|
305
|
+
as_role=self.as_role,
|
290
306
|
)
|
291
307
|
except Exception as e:
|
292
308
|
logging.error("Failed to set marketdata endpoint: %s", e)
|
@@ -304,6 +320,8 @@ class AsyncClient:
|
|
304
320
|
port=grpc_port,
|
305
321
|
use_ssl=use_ssl,
|
306
322
|
options=self.grpc_options,
|
323
|
+
as_user=self.as_user,
|
324
|
+
as_role=self.as_role,
|
307
325
|
)
|
308
326
|
logging.debug(
|
309
327
|
f"Setting marketdata endpoint for {venue}: {grpc_host}:{grpc_port} use_ssl={use_ssl}"
|
@@ -340,6 +358,8 @@ class AsyncClient:
|
|
340
358
|
port=grpc_port,
|
341
359
|
use_ssl=use_ssl,
|
342
360
|
options=self.grpc_options,
|
361
|
+
as_user=self.as_user,
|
362
|
+
as_role=self.as_role,
|
343
363
|
)
|
344
364
|
except Exception as e:
|
345
365
|
logging.error("Failed to set hmart endpoint: %s", e)
|
@@ -380,6 +400,12 @@ class AsyncClient:
|
|
380
400
|
res = await self.graphql_client.user_id_query()
|
381
401
|
return res.user_id, res.user_email
|
382
402
|
|
403
|
+
async def auth_info(self) -> AuthInfoResponse:
|
404
|
+
grpc_client = await self.core()
|
405
|
+
req = AuthInfoRequest()
|
406
|
+
res: AuthInfoResponse = await grpc_client.unary_unary(req)
|
407
|
+
return res
|
408
|
+
|
383
409
|
def enable_orderflow(self):
|
384
410
|
"""
|
385
411
|
@deprecated(reason="No longer needed; orderflow is enabled by default")
|
@@ -864,8 +890,43 @@ class AsyncClient:
|
|
864
890
|
res: Ticker = await grpc_client.unary_unary(req)
|
865
891
|
return res
|
866
892
|
|
893
|
+
async def get_tickers(
|
894
|
+
self,
|
895
|
+
*,
|
896
|
+
venue: Venue,
|
897
|
+
symbols: Optional[Sequence[TradableProduct | str]] = None,
|
898
|
+
include_options: bool = False,
|
899
|
+
sort_by: Optional[SortTickersBy | str] = None,
|
900
|
+
offset: Optional[int] = None,
|
901
|
+
limit: Optional[int] = None,
|
902
|
+
as_dataframe: bool = False,
|
903
|
+
) -> Union[Sequence[Ticker], pd.DataFrame]:
|
904
|
+
"""
|
905
|
+
Gets the tickers for a list of symbols.
|
906
|
+
"""
|
907
|
+
grpc_client = await self.marketdata(venue)
|
908
|
+
sort_by = SortTickersBy(sort_by) if sort_by else None
|
909
|
+
symbols = [str(symbol) for symbol in symbols] if symbols else None
|
910
|
+
req = TickersRequest.new(
|
911
|
+
offset=offset,
|
912
|
+
include_options=include_options,
|
913
|
+
sort_by=sort_by,
|
914
|
+
limit=limit,
|
915
|
+
symbols=symbols,
|
916
|
+
venue=venue,
|
917
|
+
)
|
918
|
+
res: TickersResponse = await grpc_client.unary_unary(req)
|
919
|
+
if as_dataframe:
|
920
|
+
return tickers_to_dataframe(res.tickers)
|
921
|
+
else:
|
922
|
+
return res.tickers
|
923
|
+
|
867
924
|
async def stream_l1_book_snapshots(
|
868
|
-
self,
|
925
|
+
self,
|
926
|
+
symbols: Sequence[TradableProduct | str],
|
927
|
+
venue: Venue,
|
928
|
+
*,
|
929
|
+
send_initial_snapshots: Optional[bool] = False,
|
869
930
|
) -> AsyncGenerator[L1BookSnapshot, None]:
|
870
931
|
"""
|
871
932
|
Subscribe to the stream of L1BookSnapshots for a symbol.
|
@@ -876,7 +937,11 @@ class AsyncClient:
|
|
876
937
|
venue: the venue to subscribe to
|
877
938
|
"""
|
878
939
|
grpc_client = await self.marketdata(venue)
|
879
|
-
req = SubscribeL1BookSnapshotsRequest(
|
940
|
+
req = SubscribeL1BookSnapshotsRequest(
|
941
|
+
symbols=list(symbols),
|
942
|
+
venue=venue,
|
943
|
+
send_initial_snapshots=send_initial_snapshots,
|
944
|
+
)
|
880
945
|
async for res in grpc_client.unary_stream(req):
|
881
946
|
yield res
|
882
947
|
|
@@ -1117,6 +1182,27 @@ class AsyncClient:
|
|
1117
1182
|
res = await grpc_client.unary_unary(req)
|
1118
1183
|
return res
|
1119
1184
|
|
1185
|
+
async def get_positions(
|
1186
|
+
self,
|
1187
|
+
accounts: Optional[list[str]] = None,
|
1188
|
+
trader: Optional[str] = None,
|
1189
|
+
) -> dict[str, Decimal]:
|
1190
|
+
"""
|
1191
|
+
Get positions for the specified symbols.
|
1192
|
+
|
1193
|
+
Args:
|
1194
|
+
symbols: list of symbol strings
|
1195
|
+
"""
|
1196
|
+
account_summaries = await self.get_account_summaries(
|
1197
|
+
accounts=accounts, trader=trader
|
1198
|
+
)
|
1199
|
+
positions: dict[str, Decimal] = {}
|
1200
|
+
for summary in account_summaries:
|
1201
|
+
for symbol, summary in summary.positions.items():
|
1202
|
+
for pos in summary:
|
1203
|
+
positions[symbol] = positions.get(symbol, Decimal(0)) + pos.quantity
|
1204
|
+
return positions
|
1205
|
+
|
1120
1206
|
async def get_account_summaries(
|
1121
1207
|
self,
|
1122
1208
|
accounts: Optional[list[str]] = None,
|
@@ -1381,30 +1467,17 @@ class AsyncClient:
|
|
1381
1467
|
|
1382
1468
|
async def orderflow(
|
1383
1469
|
self,
|
1384
|
-
|
1385
|
-
) ->
|
1470
|
+
max_queue_size: int = 1024,
|
1471
|
+
) -> OrderflowChannel:
|
1386
1472
|
"""
|
1387
1473
|
A two-way channel for both order entry and listening to order updates (fills, acks, outs, etc.).
|
1388
1474
|
|
1389
1475
|
This is considered the most efficient way to trade in this SDK.
|
1390
1476
|
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
This WILL block the event loop until the stream is closed.
|
1477
|
+
This requires advanced knowledge of the SDK and asyncio, not recommended for beginners.
|
1478
|
+
See the OrderflowManager documentation for more details.
|
1395
1479
|
"""
|
1396
|
-
|
1397
|
-
decoder = grpc_client.get_decoder(OrderflowRequestUnannotatedResponseType)
|
1398
|
-
stub: grpc.aio.StreamStreamMultiCallable = grpc_client.channel.stream_stream(
|
1399
|
-
OrderflowRequest_route,
|
1400
|
-
request_serializer=grpc_client.encoder().encode,
|
1401
|
-
response_deserializer=decoder.decode,
|
1402
|
-
)
|
1403
|
-
call: grpc.aio._base_call.StreamStreamCall[OrderflowRequest, Orderflow] = stub(
|
1404
|
-
request_iterator, metadata=(("authorization", f"Bearer {grpc_client.jwt}"),)
|
1405
|
-
)
|
1406
|
-
async for update in call:
|
1407
|
-
yield update
|
1480
|
+
return OrderflowChannel(self, max_queue_size=max_queue_size)
|
1408
1481
|
|
1409
1482
|
async def stream_orderflow(
|
1410
1483
|
self,
|
@@ -1438,9 +1511,27 @@ class AsyncClient:
|
|
1438
1511
|
**kwargs,
|
1439
1512
|
) -> Order:
|
1440
1513
|
"""
|
1441
|
-
@deprecated(reason="Use
|
1514
|
+
@deprecated(reason="Use place_order instead")
|
1515
|
+
"""
|
1516
|
+
logging.warning(
|
1517
|
+
"send_limit_order is deprecated, use place_order instead. "
|
1518
|
+
"This will be removed in a future version."
|
1519
|
+
)
|
1520
|
+
return await self.place_order(*args, **kwargs)
|
1521
|
+
|
1522
|
+
async def place_limit_order(
|
1523
|
+
self,
|
1524
|
+
*args,
|
1525
|
+
**kwargs,
|
1526
|
+
) -> Order:
|
1527
|
+
"""
|
1528
|
+
@deprecated(reason="Use place_order instead")
|
1442
1529
|
"""
|
1443
|
-
|
1530
|
+
logging.warning(
|
1531
|
+
"place_limit_order is deprecated, use place_order instead. "
|
1532
|
+
"This will be removed in a future version."
|
1533
|
+
)
|
1534
|
+
return await self.place_order(*args, **kwargs)
|
1444
1535
|
|
1445
1536
|
async def place_orders(
|
1446
1537
|
self, order_requests: Sequence[PlaceOrderRequest]
|
@@ -1484,26 +1575,28 @@ class AsyncClient:
|
|
1484
1575
|
|
1485
1576
|
return res
|
1486
1577
|
|
1487
|
-
async def
|
1578
|
+
async def place_order(
|
1488
1579
|
self,
|
1489
1580
|
*,
|
1490
1581
|
id: Optional[OrderId] = None,
|
1491
1582
|
symbol: TradableProduct | str,
|
1492
1583
|
execution_venue: Optional[str] = None,
|
1493
|
-
dir:
|
1584
|
+
dir: OrderDir,
|
1494
1585
|
quantity: Decimal,
|
1495
|
-
limit_price: Decimal,
|
1586
|
+
limit_price: Optional[Decimal] = None,
|
1496
1587
|
order_type: OrderType = OrderType.LIMIT,
|
1497
1588
|
time_in_force: TimeInForce = TimeInForce.DAY,
|
1498
1589
|
price_round_method: Optional[TickRoundMethod] = None,
|
1499
1590
|
account: Optional[str] = None,
|
1500
1591
|
trader: Optional[str] = None,
|
1501
|
-
post_only: bool =
|
1592
|
+
post_only: Optional[bool] = None,
|
1502
1593
|
trigger_price: Optional[Decimal] = None,
|
1594
|
+
stop_loss: Optional[TriggerLimitOrderType] = None,
|
1595
|
+
take_profit_price: Optional[Decimal] = None,
|
1503
1596
|
**kwargs: Any,
|
1504
1597
|
) -> Order:
|
1505
1598
|
"""
|
1506
|
-
Sends a regular
|
1599
|
+
Sends a regular order.
|
1507
1600
|
|
1508
1601
|
Args:
|
1509
1602
|
id: in case user wants to generate their own order id, otherwise it will be generated automatically
|
@@ -1522,8 +1615,10 @@ class AsyncClient:
|
|
1522
1615
|
While technically optional, for most order types, the account is required
|
1523
1616
|
trader: the trader to send the order for, defaults to the user's trader
|
1524
1617
|
for when sending order for another user, not relevant for vast majority of users
|
1525
|
-
post_only: whether the order should be post only,
|
1618
|
+
post_only: whether the order should be post only, NOT SUPPORTED BY ALL EXCHANGES (e.g. CME)
|
1526
1619
|
trigger_price: the trigger price for the order, only relevant for stop / take_profit orders
|
1620
|
+
stop_loss_price: the stop loss price for a bracket order.
|
1621
|
+
profit_price: the take profit price for a bracket order.
|
1527
1622
|
Returns:
|
1528
1623
|
the Order object for the order
|
1529
1624
|
The order.status should be "PENDING" until the order is "OPEN" / "REJECTED" / "OUT" / "CANCELED" / "STALE"
|
@@ -1533,14 +1628,7 @@ class AsyncClient:
|
|
1533
1628
|
grpc_client = await self.core()
|
1534
1629
|
assert quantity > 0, "quantity must be positive"
|
1535
1630
|
|
1536
|
-
if
|
1537
|
-
if "odir" in kwargs and isinstance(kwargs["odir"], OrderDir):
|
1538
|
-
logging.warning("odir is deprecated, use dir instead")
|
1539
|
-
dir = kwargs["odir"]
|
1540
|
-
else:
|
1541
|
-
raise ValueError("dir is required")
|
1542
|
-
|
1543
|
-
if price_round_method is not None:
|
1631
|
+
if limit_price is not None and price_round_method is not None:
|
1544
1632
|
if execution_venue is None:
|
1545
1633
|
product_info = await self.get_product_info(symbol)
|
1546
1634
|
if product_info is None:
|
@@ -1580,6 +1668,8 @@ class AsyncClient:
|
|
1580
1668
|
execution_venue=execution_venue,
|
1581
1669
|
post_only=post_only,
|
1582
1670
|
trigger_price=trigger_price,
|
1671
|
+
stop_loss=stop_loss,
|
1672
|
+
take_profit_price=take_profit_price,
|
1583
1673
|
)
|
1584
1674
|
res = await grpc_client.unary_unary(req)
|
1585
1675
|
return res
|
@@ -1724,3 +1814,26 @@ class AsyncClient:
|
|
1724
1814
|
# )
|
1725
1815
|
# res = await grpc_client.unary_unary(req)
|
1726
1816
|
# return True
|
1817
|
+
|
1818
|
+
async def create_algo_order(
|
1819
|
+
self,
|
1820
|
+
*,
|
1821
|
+
params: SpreaderParams,
|
1822
|
+
id: Optional[str] = None,
|
1823
|
+
trader: Optional[str] = None,
|
1824
|
+
):
|
1825
|
+
"""
|
1826
|
+
Sends an advanced algo order such as the spreader.
|
1827
|
+
"""
|
1828
|
+
grpc_client = await self.core()
|
1829
|
+
|
1830
|
+
if isinstance(params, SpreaderParams):
|
1831
|
+
algo = "SPREADER"
|
1832
|
+
else:
|
1833
|
+
raise ValueError(
|
1834
|
+
"Unsupported algo type. Only SpreaderParams is supported for now."
|
1835
|
+
)
|
1836
|
+
|
1837
|
+
req = CreateAlgoOrderRequest(algo=algo, params=params, id=id, trader=trader)
|
1838
|
+
res = await grpc_client.unary_unary(req)
|
1839
|
+
return res
|
architect_py/client.py
CHANGED
@@ -56,6 +56,8 @@ class Client:
|
|
56
56
|
api_key: str,
|
57
57
|
api_secret: str,
|
58
58
|
paper_trading: bool,
|
59
|
+
as_user: Optional[str] = None,
|
60
|
+
as_role: Optional[str] = None,
|
59
61
|
endpoint: str = "https://app.architect.co",
|
60
62
|
graphql_port: Optional[int] = None,
|
61
63
|
grpc_options: Sequence[tuple[str, Any]] | None = None,
|
@@ -85,6 +87,8 @@ class Client:
|
|
85
87
|
api_key=api_key,
|
86
88
|
api_secret=api_secret,
|
87
89
|
paper_trading=paper_trading,
|
90
|
+
as_user=as_user,
|
91
|
+
as_role=as_role,
|
88
92
|
endpoint=endpoint,
|
89
93
|
graphql_port=graphql_port,
|
90
94
|
grpc_options=grpc_options,
|
architect_py/client.pyi
CHANGED
@@ -9,17 +9,17 @@ from architect_py.graphql_client import GraphQLClient as GraphQLClient
|
|
9
9
|
from architect_py.graphql_client.exceptions import GraphQLClientGraphQLMultiError as GraphQLClientGraphQLMultiError
|
10
10
|
from architect_py.graphql_client.fragments import ExecutionInfoFields as ExecutionInfoFields, ProductInfoFields as ProductInfoFields
|
11
11
|
from architect_py.grpc.client import GrpcClient as GrpcClient
|
12
|
-
from architect_py.grpc.models.
|
13
|
-
from architect_py.grpc.
|
12
|
+
from architect_py.grpc.models.definitions import AccountIdOrName as AccountIdOrName, AccountWithPermissions as AccountWithPermissions, CandleWidth as CandleWidth, L2BookDiff as L2BookDiff, OrderId as OrderId, OrderSource as OrderSource, OrderType as OrderType, SortTickersBy as SortTickersBy, SpreaderParams as SpreaderParams, TraderIdOrEmail as TraderIdOrEmail, TriggerLimitOrderType as TriggerLimitOrderType
|
13
|
+
from architect_py.grpc.orderflow import OrderflowChannel as OrderflowChannel
|
14
14
|
from architect_py.grpc.resolve_endpoint import PAPER_GRPC_PORT as PAPER_GRPC_PORT, resolve_endpoint as resolve_endpoint
|
15
15
|
from architect_py.utils.nearest_tick import TickRoundMethod as TickRoundMethod
|
16
16
|
from architect_py.utils.orderbook import update_orderbook_side as update_orderbook_side
|
17
|
-
from architect_py.utils.pandas import candles_to_dataframe as candles_to_dataframe
|
17
|
+
from architect_py.utils.pandas import candles_to_dataframe as candles_to_dataframe, tickers_to_dataframe as tickers_to_dataframe
|
18
18
|
from architect_py.utils.price_bands import price_band_pairs as price_band_pairs
|
19
19
|
from architect_py.utils.symbol_parsing import nominative_expiration as nominative_expiration
|
20
20
|
from datetime import date, datetime
|
21
21
|
from decimal import Decimal
|
22
|
-
from typing import Any, AsyncGenerator,
|
22
|
+
from typing import Any, AsyncGenerator, Literal, Sequence, overload
|
23
23
|
|
24
24
|
class Client:
|
25
25
|
"""
|
@@ -37,6 +37,8 @@ class Client:
|
|
37
37
|
api_key: str | None
|
38
38
|
api_secret: str | None
|
39
39
|
paper_trading: bool
|
40
|
+
as_user: str | None
|
41
|
+
as_role: str | None
|
40
42
|
graphql_client: GraphQLClient
|
41
43
|
grpc_options: Sequence[tuple[str, Any]] | None
|
42
44
|
grpc_core: GrpcClient | None
|
@@ -45,7 +47,7 @@ class Client:
|
|
45
47
|
jwt: str | None
|
46
48
|
jwt_expiration: datetime | None
|
47
49
|
l1_books: dict[Venue, dict[TradableProduct, tuple[L1BookSnapshot, asyncio.Task]]]
|
48
|
-
def __init__(self, *, api_key: str, api_secret: str, paper_trading: bool, endpoint: str = 'https://app.architect.co', graphql_port: int | None = None, grpc_options: Sequence[tuple[str, Any]] | None = None, event_loop: asyncio.events.AbstractEventLoop | None = None) -> None:
|
50
|
+
def __init__(self, *, api_key: str, api_secret: str, paper_trading: bool, as_user: str | None = None, as_role: str | None = None, endpoint: str = 'https://app.architect.co', graphql_port: int | None = None, grpc_options: Sequence[tuple[str, Any]] | None = None, event_loop: asyncio.events.AbstractEventLoop | None = None) -> None:
|
49
51
|
"""
|
50
52
|
Create a new Client instance.
|
51
53
|
|
@@ -119,6 +121,7 @@ class Client:
|
|
119
121
|
Returns:
|
120
122
|
(user_id, user_email)
|
121
123
|
"""
|
124
|
+
def auth_info(self) -> AuthInfoResponse: ...
|
122
125
|
def cpty_status(self, kind: str, instance: str | None = None) -> CptyStatus:
|
123
126
|
"""
|
124
127
|
Get cpty status.
|
@@ -350,6 +353,10 @@ class Client:
|
|
350
353
|
"""
|
351
354
|
Gets the ticker for a symbol.
|
352
355
|
"""
|
356
|
+
def get_tickers(self, *, venue: Venue, symbols: Sequence[TradableProduct | str] | None = None, include_options: bool = False, sort_by: SortTickersBy | str | None = None, offset: int | None = None, limit: int | None = None, as_dataframe: bool = False) -> Sequence[Ticker] | pd.DataFrame:
|
357
|
+
"""
|
358
|
+
Gets the tickers for a list of symbols.
|
359
|
+
"""
|
353
360
|
def list_accounts(self) -> list[AccountWithPermissions]:
|
354
361
|
"""
|
355
362
|
List accounts for the user that the API key belongs to.
|
@@ -367,6 +374,13 @@ class Client:
|
|
367
374
|
account: account uuid or name
|
368
375
|
Examples: "00000000-0000-0000-0000-000000000000", "STONEX:000000/JDoe"
|
369
376
|
'''
|
377
|
+
def get_positions(self, accounts: list[str] | None = None, trader: str | None = None) -> dict[str, Decimal]:
|
378
|
+
"""
|
379
|
+
Get positions for the specified symbols.
|
380
|
+
|
381
|
+
Args:
|
382
|
+
symbols: list of symbol strings
|
383
|
+
"""
|
370
384
|
def get_account_summaries(self, accounts: list[str] | None = None, trader: str | None = None) -> list[AccountSummary]:
|
371
385
|
"""
|
372
386
|
Get account summaries for accounts matching the filters.
|
@@ -451,7 +465,11 @@ class Client:
|
|
451
465
|
'''
|
452
466
|
def send_limit_order(self, *args, **kwargs) -> Order:
|
453
467
|
'''
|
454
|
-
@deprecated(reason="Use
|
468
|
+
@deprecated(reason="Use place_order instead")
|
469
|
+
'''
|
470
|
+
def place_limit_order(self, *args, **kwargs) -> Order:
|
471
|
+
'''
|
472
|
+
@deprecated(reason="Use place_order instead")
|
455
473
|
'''
|
456
474
|
def place_orders(self, order_requests: Sequence[PlaceOrderRequest]) -> list[Order]:
|
457
475
|
"""
|
@@ -482,9 +500,9 @@ class Client:
|
|
482
500
|
trigger_price=trigger_price,
|
483
501
|
)
|
484
502
|
"""
|
485
|
-
def
|
503
|
+
def place_order(self, *, id: OrderId | None = None, symbol: TradableProduct | str, execution_venue: str | None = None, dir: OrderDir, quantity: Decimal, limit_price: Decimal | None = None, order_type: OrderType = ..., time_in_force: TimeInForce = ..., price_round_method: TickRoundMethod | None = None, account: str | None = None, trader: str | None = None, post_only: bool | None = None, trigger_price: Decimal | None = None, stop_loss: TriggerLimitOrderType | None = None, take_profit_price: Decimal | None = None, **kwargs: Any) -> Order:
|
486
504
|
'''
|
487
|
-
Sends a regular
|
505
|
+
Sends a regular order.
|
488
506
|
|
489
507
|
Args:
|
490
508
|
id: in case user wants to generate their own order id, otherwise it will be generated automatically
|
@@ -503,8 +521,10 @@ class Client:
|
|
503
521
|
While technically optional, for most order types, the account is required
|
504
522
|
trader: the trader to send the order for, defaults to the user\'s trader
|
505
523
|
for when sending order for another user, not relevant for vast majority of users
|
506
|
-
post_only: whether the order should be post only,
|
524
|
+
post_only: whether the order should be post only, NOT SUPPORTED BY ALL EXCHANGES (e.g. CME)
|
507
525
|
trigger_price: the trigger price for the order, only relevant for stop / take_profit orders
|
526
|
+
stop_loss_price: the stop loss price for a bracket order.
|
527
|
+
profit_price: the take profit price for a bracket order.
|
508
528
|
Returns:
|
509
529
|
the Order object for the order
|
510
530
|
The order.status should be "PENDING" until the order is "OPEN" / "REJECTED" / "OUT" / "CANCELED" / "STALE"
|
@@ -549,3 +569,7 @@ class Client:
|
|
549
569
|
True if all orders were cancelled successfully
|
550
570
|
False if there was an error
|
551
571
|
"""
|
572
|
+
def create_algo_order(self, *, params: SpreaderParams, id: str | None = None, trader: str | None = None):
|
573
|
+
"""
|
574
|
+
Sends an advanced algo order such as the spreader.
|
575
|
+
"""
|