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.
- tradx/__init__.py +2 -0
- tradx/algoContainer.py +90 -0
- tradx/baseClass/baseAlgo.py +338 -0
- tradx/baseClass/candleData.py +89 -0
- tradx/baseClass/cmInstrument.py +84 -0
- tradx/baseClass/futureInstrument.py +74 -0
- tradx/baseClass/index.py +24 -0
- tradx/baseClass/instrumentPropertyChangeData.py +14 -0
- tradx/baseClass/ltpData.py +89 -0
- tradx/baseClass/ltpPartialData.py +108 -0
- tradx/baseClass/marketDepthData.py +126 -0
- tradx/baseClass/marketStatusData.py +110 -0
- tradx/baseClass/openInterestData.py +84 -0
- tradx/baseClass/openInterestPartialData.py +47 -0
- tradx/baseClass/optionsInstrument.py +279 -0
- tradx/baseClass/order.py +27 -0
- tradx/baseClass/orderEvent.py +90 -0
- tradx/baseClass/position.py +46 -0
- tradx/baseClass/positionEvent.py +84 -0
- tradx/baseClass/touchLineData.py +201 -0
- tradx/baseClass/touchLinePartialData.py +109 -0
- tradx/baseClass/tradeConversionEvent.py +80 -0
- tradx/baseClass/tradeEvent.py +153 -0
- tradx/constants/holidays.py +32 -0
- tradx/dualHashMap.py +57 -0
- tradx/interactiveEngine.py +764 -0
- tradx/logger/logger.py +83 -0
- tradx/logger/logger2.py +73 -0
- tradx/marketDataEngine.py +781 -0
- tradx/py.typed +0 -0
- tradx-0.1.0.dist-info/METADATA +69 -0
- tradx-0.1.0.dist-info/RECORD +33 -0
- tradx-0.1.0.dist-info/WHEEL +4 -0
@@ -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
|
+
)
|