tradx 0.1.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.
@@ -0,0 +1,764 @@
1
+ from xts_api_client.xts_connect_async import XTSConnect
2
+ from xts_api_client.interactive_socket_client import InteractiveSocketClient
3
+ from xts_api_client.interactive_socket import OrderSocket_io
4
+ from decimal import Decimal
5
+ from typing import Dict, Any
6
+ import asyncio
7
+ import shortuuid
8
+ from tradx.logger.logger import *
9
+ from tradx.baseClass.baseAlgo import BaseAlgo as bA
10
+ from tradx.baseClass.orderEvent import OrderEvent
11
+ from tradx.baseClass.positionEvent import PositionEvent
12
+ from tradx.baseClass.tradeEvent import TradeEvent
13
+ from tradx.baseClass.tradeConversionEvent import TradeConversionEvent
14
+
15
+
16
+ class interactiveEngine(InteractiveSocketClient):
17
+ """
18
+ interactiveEngine class for handling interactive trading operations.
19
+ This class extends the InteractiveSocketClient and provides methods for placing various types of orders,
20
+ handling incoming messages, and managing the interactive trading session.
21
+ Methods:
22
+ __init__(self, user_logger: logging.Logger = None) -> None:
23
+ async market_order(self, exchangeSegment: str, exchangeInstrumentID: int, productType: str, orderQuantity: int, orderUniqueIdentifier: str) -> None:
24
+ async stop_market_order(self, exchangeSegment: str, exchangeInstrumentID: int, productType: str, orderQuantity: int, stopPrice: Decimal, orderUniqueIdentifier: str) -> None:
25
+ async limit_order(self, exchangeSegment: str, exchangeInstrumentID: int, productType: str, orderQuantity: int, limitPrice: Decimal, orderUniqueIdentifier: str) -> None:
26
+ async stop_limit_order(self, exchangeSegment: str, exchangeInstrumentID: int, productType: str, orderQuantity: int, limitPrice: Decimal, stopPrice: Decimal, orderUniqueIdentifier: str) -> None:
27
+ async initialize(self) -> None:
28
+ async on_order(self, message: str) -> None:
29
+ async on_connect(self) -> None:
30
+ async on_message(self, xts_message: Any) -> None:
31
+ async on_error(self, xts_message: Any) -> None:
32
+ async on_joined(self, data: Any) -> None:
33
+ async on_messagelogout(self, data: Any) -> None:
34
+ async on_disconnect(self) -> None:
35
+ async on_trade(self, xts_message: str) -> None:
36
+ async on_position(self, xts_message: str) -> None:
37
+ async on_tradeconversion(self, xts_message: str) -> None:
38
+ async login(self) -> None:
39
+ async shutdown(self) -> None:
40
+ async getBalance(self) -> int:
41
+ async Liquidate(self, exchangeSegment: str) -> None:
42
+ async cancel_order(self, appOrderID: Any, orderUniqueIdentifier: str) -> None
43
+ """
44
+
45
+ def __init__(
46
+ self,
47
+ api_key: str,
48
+ api_password: str,
49
+ source: str,
50
+ root: str,
51
+ user_logger: logging.Logger = None,
52
+ ) -> None:
53
+ """
54
+ Initialize the InteractiveEngine object.
55
+ Args:
56
+ api_key (str): The API key required for authentication.
57
+ api_password (str): The API password required for authentication.
58
+ source (str): The source identifier.
59
+ root (str): The root directory path.
60
+ user_logger (logging.Logger, optional): Logger instance for user logging. Defaults to None.
61
+ Raises:
62
+ AssertionError: If any of the required arguments (api_key, api_password, source, root) are not provided.
63
+ Attributes:
64
+ _api_key (str): Internal API key.
65
+ _api_password (str): Internal API password.
66
+ _source (str): Internal source identifier.
67
+ _root (str): Internal root directory path.
68
+ api_key (str): Public API key.
69
+ api_password (str): Public API password.
70
+ source (str): Public source identifier.
71
+ root (str): Public root directory path.
72
+ shortuuid (shortuuid): Short UUID instance.
73
+ set_interactiveToken (str): Interactive token, initially set to None.
74
+ set_iuserID (str): User ID, initially set to None.
75
+ funds (int): User funds, initially set to None.
76
+ strategy_to_id (Dict[str, BaseAlgo]): Mapping of strategy names to BaseAlgo instances.
77
+ position_diary_engine (dict): Position diary engine, initially an empty dictionary.
78
+ user_logger (logging.Logger): Logger instance for user logging.
79
+ """
80
+ assert api_key, "API key is required"
81
+ assert api_password, "API password is required"
82
+ assert source, "Source is required"
83
+ assert root, "Root is required"
84
+ self._api_key: str = api_key
85
+ self._api_password: str = api_password
86
+ self._source: str = source
87
+ self._root: str = root
88
+ self.shortuuid: shortuuid = shortuuid
89
+ self.set_interactiveToken: str = None
90
+ self.set_iuserID: str = None
91
+ self.funds: int = None
92
+ self.strategy_to_id: Dict[str, bA] = {}
93
+ self.position_diary_engine = {}
94
+ self.user_logger = user_logger
95
+ if user_logger:
96
+ self.user_logger.info(
97
+ "Interactive Engine Object initialized.",
98
+ caller="interactiveEngine.__init__",
99
+ )
100
+
101
+ async def market_order(
102
+ self,
103
+ exchangeSegment: str,
104
+ exchangeInstrumentID: int,
105
+ productType: str,
106
+ orderQuantity: int,
107
+ orderUniqueIdentifier: str,
108
+ ) -> None:
109
+ """
110
+ Places a market order on the specified exchange.
111
+ Args:
112
+ exchangeSegment (str): The segment of the exchange where the order is to be placed. Allowed values: xts_api_client.xts_connect_async.EXCHANGE_NSECM, xts_api_client.xts_connect_async.EXCHANGE_BSECM, xts_api_client.xts_connect_async.EXCHANGE_NSEFO, xts_api_client.xts_connect_async.EXCHANGE_BSEFO, xts_api_client.xts_connect_async.EXCHANGE_NSECD, xts_api_client.xts_connect_async.EXCHANGE_MCXFO.
113
+ exchangeInstrumentID (int): The ID of the instrument to be traded.
114
+ productType (str): The type of product being traded. Allowed values: xts_api_client.xts_connect_async.PRODUCT_MIS, xts_api_client.xts_connect_async.PRODUCT_NRML.
115
+ orderQuantity (int): The quantity of the order. Positive for buy, negative for sell.
116
+ orderUniqueIdentifier (str): A unique identifier for the order.
117
+ Returns:
118
+ None
119
+ Notes:
120
+ - If the orderQuantity is 0, the function will return immediately without placing an order.
121
+ - The order type is set to market order.
122
+ - The order side is determined based on the sign of orderQuantity.
123
+ - The time in force is set to day validity.
124
+ - The disclosed quantity, limit price, and stop price are set to 0.
125
+ - Logs the response if user_logger is available.
126
+ """
127
+ allowed_exchange_segments = [
128
+ XTSConnect.EXCHANGE_NSECM,
129
+ XTSConnect.EXCHANGE_BSECM,
130
+ XTSConnect.EXCHANGE_NSEFO,
131
+ XTSConnect.EXCHANGE_BSEFO,
132
+ XTSConnect.EXCHANGE_NSECD,
133
+ XTSConnect.EXCHANGE_MCXFO,
134
+ ]
135
+ allowed_product_types = [XTSConnect.PRODUCT_MIS, XTSConnect.PRODUCT_NRML, "CNC"]
136
+ assert (
137
+ exchangeSegment in allowed_exchange_segments
138
+ ), f"Invalid exchangeSegment: {exchangeSegment}"
139
+ assert (
140
+ productType in allowed_product_types
141
+ ), f"Invalid productType: {productType}"
142
+ if orderQuantity == 0:
143
+ return
144
+ response = await self.xt.place_order(
145
+ exchangeSegment=exchangeSegment,
146
+ exchangeInstrumentID=exchangeInstrumentID,
147
+ productType=productType,
148
+ orderType=self.xt.ORDER_TYPE_MARKET,
149
+ orderSide=(
150
+ self.xt.TRANSACTION_TYPE_BUY
151
+ if orderQuantity > 0
152
+ else self.xt.TRANSACTION_TYPE_SELL
153
+ ),
154
+ timeInForce=self.xt.VALIDITY_DAY,
155
+ disclosedQuantity=0,
156
+ orderQuantity=abs(orderQuantity),
157
+ limitPrice=0,
158
+ stopPrice=0,
159
+ orderUniqueIdentifier=orderUniqueIdentifier,
160
+ clientID=self.set_iuserID,
161
+ )
162
+ if self.user_logger:
163
+ self.user_logger.info(
164
+ f"Place Order: {response}",
165
+ caller="interactiveEngine.market_order",
166
+ )
167
+
168
+ async def stop_market_order(
169
+ self,
170
+ exchangeSegment: str,
171
+ exchangeInstrumentID: int,
172
+ productType: str,
173
+ orderQuantity: int,
174
+ stopPrice: Decimal,
175
+ orderUniqueIdentifier: str,
176
+ ) -> None:
177
+ """
178
+ Places a stop market order on the specified exchange.
179
+ Args:
180
+ exchangeSegment (str): The segment of the exchange where the order is to be placed. Allowed values: xts_api_client.xts_connect_async.EXCHANGE_NSECM, xts_api_client.xts_connect_async.EXCHANGE_BSECM, xts_api_client.xts_connect_async.EXCHANGE_NSEFO, xts_api_client.xts_connect_async.EXCHANGE_BSEFO, xts_api_client.xts_connect_async.EXCHANGE_NSECD, xts_api_client.xts_connect_async.EXCHANGE_MCXFO.
181
+ exchangeInstrumentID (int): The ID of the instrument to be traded.
182
+ productType (str): The type of product being traded. Allowed values: xts_api_client.xts_connect_async.PRODUCT_MIS, xts_api_client.xts_connect_async.PRODUCT_NRML.
183
+ orderQuantity (int): The quantity of the order. Positive for buy, negative for sell.
184
+ stopPrice (Decimal): The stop price for the order.
185
+ orderUniqueIdentifier (str): A unique identifier for the order.
186
+ Returns:
187
+ None
188
+ Notes:
189
+ - If orderQuantity is 0, the function will return immediately without placing an order.
190
+ - The order type is set to stop market.
191
+ - The order side is determined based on the sign of orderQuantity.
192
+ - The time in force is set to day validity.
193
+ - The disclosed quantity is set to 0.
194
+ - The limit price is set to 0.
195
+ - Logs the response if user_logger is available.
196
+ """
197
+ allowed_exchange_segments = [
198
+ XTSConnect.EXCHANGE_NSECM,
199
+ XTSConnect.EXCHANGE_BSECM,
200
+ XTSConnect.EXCHANGE_NSEFO,
201
+ XTSConnect.EXCHANGE_BSEFO,
202
+ XTSConnect.EXCHANGE_NSECD,
203
+ XTSConnect.EXCHANGE_MCXFO,
204
+ ]
205
+ allowed_product_types = [XTSConnect.PRODUCT_MIS, XTSConnect.PRODUCT_NRML]
206
+ assert (
207
+ exchangeSegment in allowed_exchange_segments
208
+ ), f"Invalid exchangeSegment: {exchangeSegment}"
209
+ assert (
210
+ productType in allowed_product_types
211
+ ), f"Invalid productType: {productType}"
212
+ if orderQuantity == 0:
213
+ return
214
+ response = await self.xt.place_order(
215
+ exchangeSegment=exchangeSegment,
216
+ exchangeInstrumentID=exchangeInstrumentID,
217
+ productType=productType,
218
+ orderType=self.xt.ORDER_TYPE_STOPMARKET,
219
+ orderSide=(
220
+ self.xt.TRANSACTION_TYPE_BUY
221
+ if orderQuantity > 0
222
+ else self.xt.TRANSACTION_TYPE_SELL
223
+ ),
224
+ timeInForce=self.xt.VALIDITY_DAY,
225
+ disclosedQuantity=0,
226
+ orderQuantity=abs(orderQuantity),
227
+ limitPrice=0,
228
+ stopPrice=stopPrice,
229
+ orderUniqueIdentifier=orderUniqueIdentifier,
230
+ clientID=self.set_iuserID,
231
+ )
232
+ if self.user_logger:
233
+ self.user_logger.info(
234
+ f"Place Order: {response}",
235
+ caller="interactiveEngine.stop_market_order",
236
+ )
237
+
238
+ async def limit_order(
239
+ self,
240
+ exchangeSegment: str,
241
+ exchangeInstrumentID: int,
242
+ productType: str,
243
+ orderQuantity: int,
244
+ limitPrice: Decimal,
245
+ orderUniqueIdentifier: str,
246
+ ):
247
+ """
248
+ Places a limit order on the specified exchange.
249
+ Args:
250
+ exchangeSegment (str): The segment of the exchange where the order is to be placed. Allowed values: xts_api_client.xts_connect_async.EXCHANGE_NSECM, xts_api_client.xts_connect_async.EXCHANGE_BSECM, xts_api_client.xts_connect_async.EXCHANGE_NSEFO, xts_api_client.xts_connect_async.EXCHANGE_BSEFO, xts_api_client.xts_connect_async.EXCHANGE_NSECD, xts_api_client.xts_connect_async.EXCHANGE_MCXFO.
251
+ exchangeInstrumentID (int): The ID of the instrument to be traded.
252
+ productType (str): The type of product being traded. Allowed values: xts_api_client.xts_connect_async.PRODUCT_MIS, xts_api_client.xts_connect_async.PRODUCT_NRML.
253
+ orderQuantity (int): The quantity of the order. Positive for buy, negative for sell.
254
+ limitPrice (Decimal): The limit price for the order.
255
+ orderUniqueIdentifier (str): A unique identifier for the order.
256
+ Returns:
257
+ None
258
+ Notes:
259
+ - If orderQuantity is 0, the function will return immediately without placing an order.
260
+ - Logs the response of the order placement if user_logger is set.
261
+ """
262
+ allowed_exchange_segments = [
263
+ XTSConnect.EXCHANGE_NSECM,
264
+ XTSConnect.EXCHANGE_BSECM,
265
+ XTSConnect.EXCHANGE_NSEFO,
266
+ XTSConnect.EXCHANGE_BSEFO,
267
+ XTSConnect.EXCHANGE_NSECD,
268
+ XTSConnect.EXCHANGE_MCXFO,
269
+ ]
270
+ allowed_product_types = [XTSConnect.PRODUCT_MIS, XTSConnect.PRODUCT_NRML]
271
+ assert (
272
+ exchangeSegment in allowed_exchange_segments
273
+ ), f"Invalid exchangeSegment: {exchangeSegment}"
274
+ assert (
275
+ productType in allowed_product_types
276
+ ), f"Invalid productType: {productType}"
277
+ if orderQuantity == 0:
278
+ return
279
+ response = await self.xt.place_order(
280
+ exchangeSegment=exchangeSegment,
281
+ exchangeInstrumentID=exchangeInstrumentID,
282
+ productType=productType,
283
+ orderType=self.xt.ORDER_TYPE_LIMIT,
284
+ orderSide=(
285
+ self.xt.TRANSACTION_TYPE_BUY
286
+ if orderQuantity > 0
287
+ else self.xt.TRANSACTION_TYPE_SELL
288
+ ),
289
+ timeInForce=self.xt.VALIDITY_DAY,
290
+ disclosedQuantity=0,
291
+ orderQuantity=abs(orderQuantity),
292
+ limitPrice=limitPrice,
293
+ stopPrice=0,
294
+ orderUniqueIdentifier=orderUniqueIdentifier,
295
+ clientID=self.set_iuserID,
296
+ )
297
+ if self.user_logger:
298
+ self.user_logger.info(
299
+ f"Place Order: {response}",
300
+ caller="interactiveEngine.limit_order",
301
+ )
302
+
303
+ async def stop_limit_order(
304
+ self,
305
+ exchangeSegment: str,
306
+ exchangeInstrumentID: int,
307
+ productType: str,
308
+ orderQuantity: int,
309
+ limitPrice: Decimal,
310
+ stopPrice: Decimal,
311
+ orderUniqueIdentifier: str,
312
+ ) -> None:
313
+ """
314
+ Places a stop limit order on the specified exchange segment.
315
+ Args:
316
+ exchangeSegment (str): The segment of the exchange where the order is to be placed. Allowed values: xts_api_client.xts_connect_async.EXCHANGE_NSECM, xts_api_client.xts_connect_async.EXCHANGE_BSECM, xts_api_client.xts_connect_async.EXCHANGE_NSEFO, xts_api_client.xts_connect_async.EXCHANGE_BSEFO, xts_api_client.xts_connect_async.EXCHANGE_NSECD, xts_api_client.xts_connect_async.EXCHANGE_MCXFO.
317
+ exchangeInstrumentID (int): The ID of the instrument to be traded.
318
+ productType (str): The type of product being traded. Allowed values: xts_api_client.xts_connect_async.PRODUCT_MIS, xts_api_client.xts_connect_async.PRODUCT_NRML.
319
+ orderQuantity (int): The quantity of the order. Positive for buy, negative for sell.
320
+ limitPrice (Decimal): The limit price for the order.
321
+ stopPrice (Decimal): The stop price for the order.
322
+ orderUniqueIdentifier (str): A unique identifier for the order.
323
+ Returns:
324
+ None
325
+ Raises:
326
+ AssertionError: If any of the input parameters are invalid.
327
+ Notes:
328
+ - The function validates the input parameters before placing the order.
329
+ - If the order quantity is zero, the function returns immediately without placing an order.
330
+ - The function logs the response from the order placement if a user logger is available.
331
+ """
332
+ allowed_exchange_segments = [
333
+ XTSConnect.EXCHANGE_NSECM,
334
+ XTSConnect.EXCHANGE_BSECM,
335
+ XTSConnect.EXCHANGE_NSEFO,
336
+ XTSConnect.EXCHANGE_BSEFO,
337
+ XTSConnect.EXCHANGE_NSECD,
338
+ XTSConnect.EXCHANGE_MCXFO,
339
+ ]
340
+ allowed_product_types = [XTSConnect.PRODUCT_MIS, XTSConnect.PRODUCT_NRML]
341
+ assert (
342
+ exchangeSegment in allowed_exchange_segments
343
+ ), f"Invalid exchangeSegment: {exchangeSegment}"
344
+ assert (
345
+ productType in allowed_product_types
346
+ ), f"Invalid productType: {productType}"
347
+ assert isinstance(exchangeSegment, str), "exchangeSegment must be a string"
348
+ assert isinstance(
349
+ exchangeInstrumentID, int
350
+ ), "exchangeInstrumentID must be an integer"
351
+ assert isinstance(productType, str), "productType must be a string"
352
+ assert isinstance(orderQuantity, int), "orderQuantity must be an integer"
353
+ assert isinstance(limitPrice, Decimal), "limitPrice must be a Decimal"
354
+ assert isinstance(stopPrice, Decimal), "stopPrice must be a Decimal"
355
+ assert isinstance(
356
+ orderUniqueIdentifier, str
357
+ ), "orderUniqueIdentifier must be a string"
358
+ if orderQuantity == 0:
359
+ return
360
+ response = await self.xt.place_order(
361
+ exchangeSegment=exchangeSegment,
362
+ exchangeInstrumentID=exchangeInstrumentID,
363
+ productType=productType,
364
+ orderType=self.xt.ORDER_TYPE_STOPLIMIT,
365
+ orderSide=(
366
+ self.xt.TRANSACTION_TYPE_BUY
367
+ if orderQuantity > 0
368
+ else self.xt.TRANSACTION_TYPE_SELL
369
+ ),
370
+ timeInForce=self.xt.VALIDITY_DAY,
371
+ disclosedQuantity=0,
372
+ orderQuantity=abs(orderQuantity),
373
+ limitPrice=limitPrice,
374
+ stopPrice=stopPrice,
375
+ orderUniqueIdentifier=orderUniqueIdentifier,
376
+ clientID=self.set_iuserID,
377
+ )
378
+ if self.user_logger:
379
+ self.user_logger.info(
380
+ f"Place Order: {response}",
381
+ caller="interactiveEngine.stop_limit_order",
382
+ )
383
+
384
+ async def initialize(self) -> None:
385
+ """
386
+ Asynchronously initializes the interactive engine by performing necessary setup tasks.
387
+ This method performs the following actions:
388
+ 1. Logs into the system.
389
+ 2. Retrieves the current balance.
390
+ Returns:
391
+ None
392
+ """
393
+
394
+ await self.login()
395
+ await self.getBalance()
396
+
397
+ async def on_order(self, message: str) -> None:
398
+ """
399
+ Handles incoming order messages.
400
+ This asynchronous method processes order messages by converting them into
401
+ an OrderEvent object and dispatching the event to the appropriate strategy
402
+ based on the order's unique identifier. It also logs the order event if a
403
+ user logger is available.
404
+ Args:
405
+ message (str): The incoming order message in string format.
406
+ Returns:
407
+ None
408
+ """
409
+
410
+ __ = OrderEvent(message)
411
+ if __.OrderUniqueIdentifier != "Liquidated":
412
+ strategy_id = __.OrderUniqueIdentifier[:4]
413
+ asyncio.ensure_future(self.strategy_to_id[strategy_id].order_(__))
414
+ if self.user_logger:
415
+ self.user_logger.info(
416
+ __,
417
+ caller="interactiveEngine.on_order",
418
+ )
419
+
420
+ async def on_connect(self) -> None:
421
+ """
422
+ Asynchronous method that handles actions to be performed upon a successful connection.
423
+ This method logs a message indicating that the interactive socket has connected successfully,
424
+ provided that a user logger is available.
425
+ Returns:
426
+ None
427
+ """
428
+
429
+ if self.user_logger:
430
+ self.user_logger.info(
431
+ "Interactive socket connected successfully!",
432
+ caller="interactiveEngine.on_connect",
433
+ )
434
+
435
+ async def on_message(self, xts_message: Any) -> None:
436
+ """
437
+ Asynchronously handles incoming messages.
438
+ This method is triggered when a new message is received. It parses the
439
+ message from JSON format and logs the message if a user logger is available.
440
+ Args:
441
+ xts_message (Any): The incoming message in JSON format.
442
+ Returns:
443
+ None
444
+ """
445
+ if self.user_logger:
446
+ self.user_logger.info(
447
+ f"Received a message: {xts_message}",
448
+ caller="interactiveEngine.on_message",
449
+ )
450
+
451
+ async def on_error(self, xts_message: Any) -> None:
452
+ """
453
+ Handles error messages received from the XTS system.
454
+ Args:
455
+ xts_message (Any): The error message received from the XTS system.
456
+ Returns:
457
+ None
458
+ """
459
+ if self.user_logger:
460
+ self.user_logger.error(
461
+ f"Received a error: {xts_message}", caller="interactiveEngine.on_error"
462
+ )
463
+
464
+ async def on_joined(self, data: Any) -> None:
465
+ """
466
+ Handles the event when the interactive socket has successfully joined.
467
+ Args:
468
+ data (Any): The data received upon joining the socket.
469
+ Logs:
470
+ Logs an informational message indicating the successful joining of the interactive socket.
471
+ """
472
+
473
+ if self.user_logger:
474
+ self.user_logger.info(
475
+ f"Interactive socket joined successfully! {data}",
476
+ caller="interactiveEngine.on_joined",
477
+ )
478
+
479
+ async def on_messagelogout(self, data: Any) -> None:
480
+ """
481
+ Callback for handling logout messages.
482
+ This method is called when a logout message is received. It logs the logout
483
+ event using the user_logger if it is available.
484
+ Args:
485
+ data (Any): The data associated with the logout message.
486
+ Returns:
487
+ None
488
+ """
489
+ if self.user_logger:
490
+ self.user_logger.info(
491
+ f"logged out! {data}", caller="interactiveEngine.on_messagelogout"
492
+ )
493
+
494
+ async def on_disconnect(self) -> None:
495
+ """
496
+ Callback for handling disconnection events.
497
+ This method is called when the interactive socket gets disconnected.
498
+ It logs an informational message indicating the disconnection event.
499
+ Returns:
500
+ None
501
+ """
502
+ if self.user_logger:
503
+ self.user_logger.info(
504
+ "Interactive Socket disconnected!",
505
+ caller="interactiveEngine.on_disconnect",
506
+ )
507
+
508
+ async def on_trade(self, xts_message: str) -> None:
509
+ """
510
+ Handle a trade event received from the exchange.
511
+ Args:
512
+ xts_message (str): The trade event message in string format.
513
+ Returns:
514
+ None
515
+ Logs:
516
+ Logs the received trade event if user_logger is set.
517
+ Actions:
518
+ - Parses the trade event message into a TradeEvent object.
519
+ - If the trade is not marked as "Liquidated", extracts the strategy ID
520
+ from the OrderUniqueIdentifier and triggers the trade_ method
521
+ of the corresponding strategy.
522
+ """
523
+
524
+ __ = TradeEvent(xts_message)
525
+ if self.user_logger:
526
+ self.user_logger.info(
527
+ f"Received a trade: {__}", caller="interactiveEngine.on_trade"
528
+ )
529
+ if __.OrderUniqueIdentifier != "Liquidated":
530
+ strategy_id = __.OrderUniqueIdentifier[:4]
531
+ await self.strategy_to_id[strategy_id].trade_(__)
532
+
533
+ async def on_position(self, xts_message: str) -> None:
534
+ """
535
+ Handles the position event received from the XTS message.
536
+ This method processes the position event, logs the received position if a user logger is available,
537
+ and updates the Interactive Engine Position Diary.
538
+ Args:
539
+ xts_message (str): The message received containing position information.
540
+ Returns:
541
+ None
542
+ """
543
+
544
+ __ = PositionEvent(xts_message)
545
+ if self.user_logger:
546
+ self.user_logger.info(
547
+ f"Received a position: {__}",
548
+ caller="interactiveEngine.on_position",
549
+ )
550
+ """Update Interactive Engine Position Diary."""
551
+ # ExchangeInstrumentID = int(on_position["ExchangeInstrumentID"])
552
+ # if ExchangeInstrumentID not in self.position_diary_engine:
553
+ # self.position_diary_engine[ExchangeInstrumentID] = 0
554
+ # self.position_diary_engine[ExchangeInstrumentID] += int(
555
+ # on_position["NetPosition"]
556
+ # )
557
+
558
+ async def on_tradeconversion(self, xts_message: str) -> None:
559
+ """
560
+ Handles the trade conversion event received from the XTS message.
561
+ This method processes the trade conversion event, logs the event if a user logger is available,
562
+ and dispatches the event to the appropriate strategy based on the order's unique identifier.
563
+ Args:
564
+ xts_message (str): The message received containing trade conversion information.
565
+ Returns:
566
+ None
567
+ """
568
+
569
+ __ = TradeConversionEvent(xts_message)
570
+ if self.user_logger:
571
+ self.user_logger.info(
572
+ f"Received a trade conversion: {__}",
573
+ caller="interactiveEngine.on_tradeconversion",
574
+ )
575
+
576
+ async def login(self) -> None:
577
+ """
578
+ Asynchronously logs in to the XTSConnect service and initializes the OrderSocket_io connection.
579
+ This method performs the following steps:
580
+ 1. Initializes the XTSConnect object with the provided API credentials.
581
+ 2. Performs an interactive login to obtain the interactive token and user ID.
582
+ 3. Logs the successful login if a user logger is available.
583
+ 4. Initializes and connects the OrderSocket_io object using the obtained token and user ID.
584
+ Raises:
585
+ Exception: If any error occurs during the login process, it is logged and re-raised.
586
+ Attributes:
587
+ self.xt (XTSConnect): The XTSConnect object initialized with API credentials.
588
+ self.set_interactiveToken (str): The token obtained from the interactive login response.
589
+ self.set_iuserID (str): The user ID obtained from the interactive login response.
590
+ self.socket (OrderSocket_io): The OrderSocket_io object initialized with the token and user ID.
591
+ """
592
+
593
+ try:
594
+ # Initialize XTSConnect object
595
+ self.xt = XTSConnect(
596
+ self._api_key, self._api_password, self._source, self._root
597
+ )
598
+
599
+ # Perform interactive login
600
+ response = await self.xt.interactive_login()
601
+ self.set_interactiveToken = response["result"]["token"]
602
+ self.set_iuserID = response["result"]["userID"]
603
+
604
+ # Log successful login
605
+ if self.user_logger:
606
+ self.user_logger.info(
607
+ f"Login successful.", caller="interactiveEngine.login"
608
+ )
609
+
610
+ # Initialize and connect OrderSocket_io object
611
+ self.socket = OrderSocket_io(
612
+ self.set_interactiveToken,
613
+ self.set_iuserID,
614
+ self._root,
615
+ self,
616
+ logger=self.user_logger,
617
+ )
618
+ await self.socket.connect()
619
+
620
+ except Exception as e:
621
+ # Log and re-raise any exceptions
622
+ if self.user_logger:
623
+ self.user_logger.error(e, caller="interactiveEngine.login")
624
+ raise (e)
625
+
626
+ async def shutdown(self) -> None:
627
+ """
628
+ Asynchronously shuts down the interactive engine.
629
+ This method performs the following steps:
630
+ 1. Logs an informational message indicating the start of the shutdown process.
631
+ 2. Disconnects the socket connection.
632
+ 3. Logs out from the interactive session.
633
+ 4. Logs informational messages indicating successful logout and end of trading.
634
+ If an exception occurs during the shutdown process, it logs the error and re-raises the exception.
635
+ Raises:
636
+ Exception: If an error occurs during the shutdown process.
637
+ Returns:
638
+ None
639
+ """
640
+
641
+ try:
642
+ # Log the start of the shutdown process
643
+ if self.user_logger:
644
+ self.user_logger.info(
645
+ "Entering shut down mode.", caller="interactiveEngine.shutdown"
646
+ )
647
+
648
+ # Disconnect the socket connection
649
+ await self.socket.disconnect()
650
+
651
+ # Log out from the interactive session
652
+ await self.xt.interactive_logout()
653
+
654
+ # Log successful logout and end of trading
655
+ if self.user_logger:
656
+ self.user_logger.info(
657
+ f"Logged Out.",
658
+ caller="interactiveEngine.shutdown",
659
+ )
660
+
661
+ except Exception as e:
662
+ # Log and re-raise any exceptions
663
+ if self.user_logger:
664
+ self.user_logger.error(e, caller="interactiveEngine.shutdown")
665
+ raise (e)
666
+
667
+ async def getBalance(self) -> int:
668
+ """
669
+ Retrieves the balance for the user.
670
+ This method is intended for retail API users only. Dealers can view their balance on the dealer terminal.
671
+ Currently, it returns a constant value for demonstration purposes.
672
+ Returns:
673
+ int: The user's balance.
674
+ """
675
+ # self.funds = self.xt.get_balance(clientID=self.set_iuserID)
676
+ self.funds = 1000000
677
+ return self.funds
678
+
679
+ async def Liquidate(self, exchangeSegment: str) -> None:
680
+ """
681
+ Liquidate open orders and square off positions for a given exchange segment.
682
+ This method cancels all open orders for the specified exchange segment and
683
+ squares off any open positions by placing market orders in the opposite direction.
684
+ Currently "CNC" type order are not allowed in liquidate.
685
+ Args:
686
+ exchangeSegment (str): The segment of the exchange where the order is to be placed. Allowed values: xts_api_client.xts_connect_async.EXCHANGE_NSECM, xts_api_client.xts_connect_async.EXCHANGE_BSECM, xts_api_client.xts_connect_async.EXCHANGE_NSEFO, xts_api_client.xts_connect_async.EXCHANGE_BSEFO, xts_api_client.xts_connect_async.EXCHANGE_NSECD, xts_api_client.xts_connect_async.EXCHANGE_MCXFO.
687
+ Returns:
688
+ None
689
+ Logs:
690
+ Logs the initiation of the liquidation process, the response from the order
691
+ cancellation, and the details of the market orders placed to square off positions.
692
+ """
693
+ # Define allowed exchange segments
694
+ allowed_exchange_segments = [
695
+ XTSConnect.EXCHANGE_NSECM,
696
+ XTSConnect.EXCHANGE_BSECM,
697
+ XTSConnect.EXCHANGE_NSEFO,
698
+ XTSConnect.EXCHANGE_BSEFO,
699
+ XTSConnect.EXCHANGE_NSECD,
700
+ XTSConnect.EXCHANGE_MCXFO,
701
+ ]
702
+
703
+ # Validate the exchange segment
704
+ assert (
705
+ exchangeSegment in allowed_exchange_segments
706
+ ), f"Invalid exchangeSegment: {exchangeSegment}"
707
+
708
+ # Log the initiation of the liquidation process
709
+ if self.user_logger:
710
+ self.user_logger.info(
711
+ f"Cancel open order and square off position for {exchangeSegment}",
712
+ caller="interactiveEngine.Liquidate",
713
+ )
714
+
715
+ # Cancel all open orders for the specified exchange segment
716
+ response = await self.xt.cancelall_order(exchangeSegment, 0)
717
+
718
+ # Log the response from the order cancellation
719
+ if self.user_logger:
720
+ self.user_logger.info(
721
+ response,
722
+ caller="interactiveEngine.Liquidate",
723
+ )
724
+
725
+ # Retrieve day-wise positions
726
+ response = await self.xt.get_position_daywise("*****")
727
+
728
+ # Square off any open positions by placing market orders in the opposite direction
729
+ for i in response["result"]["positionList"]:
730
+ if int(i["Quantity"]) != 0 and i["ProductType"] != "CNC":
731
+ asyncio.ensure_future(
732
+ self.market_order(
733
+ i["ExchangeSegment"],
734
+ int(i["ExchangeInstrumentId"]),
735
+ i["ProductType"],
736
+ -1 * int(i["Quantity"]),
737
+ "Liquidated",
738
+ )
739
+ )
740
+
741
+ async def cancel_order(self, appOrderID: Any, orderUniqueIdentifier: str) -> None:
742
+ """
743
+ Cancel an order with the given application order ID and unique order identifier.
744
+ Args:
745
+ appOrderID (Any): The application order ID to cancel.
746
+ orderUniqueIdentifier (str): The unique identifier for the order.
747
+ Returns:
748
+ None
749
+ Raises:
750
+ AssertionError: If appOrderID or orderUniqueIdentifier is None.
751
+ Logs:
752
+ Logs the response of the cancel order operation if user_logger is available.
753
+ """
754
+
755
+ assert appOrderID is not None, "appOrderID is required"
756
+ assert orderUniqueIdentifier is not None, "orderUniqueIdentifier is required"
757
+ response = await self.xt.cancel_order(
758
+ appOrderID, orderUniqueIdentifier, self.set_iuserID
759
+ )
760
+ if self.user_logger:
761
+ self.user_logger.info(
762
+ f"Cancel Order: {response}",
763
+ caller="interactiveEngine.cancel_order",
764
+ )