tradx 0.1.1.3__py3-none-any.whl → 0.1.3__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/baseClass/baseAlgo.py
CHANGED
@@ -1,17 +1,20 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
2
|
import shortuuid
|
3
|
-
from typing import Any, TYPE_CHECKING, List
|
3
|
+
from typing import Any, TYPE_CHECKING, List, Dict
|
4
|
+
from decimal import Decimal
|
4
5
|
from tradx.baseClass.order import Order
|
5
6
|
from tradx.baseClass.position import Position
|
7
|
+
|
6
8
|
import asyncio
|
7
9
|
|
8
10
|
|
9
11
|
if TYPE_CHECKING:
|
10
|
-
from tradx import
|
12
|
+
from tradx.marketDataEngine import marketDataEngine
|
13
|
+
from tradx.interactiveEngine import interactiveEngine
|
14
|
+
from tradx.baseClass.touchLineData import TouchLineData
|
11
15
|
from tradx.baseClass.orderEvent import OrderEvent
|
12
16
|
from tradx.baseClass.tradeEvent import TradeEvent
|
13
17
|
from tradx.baseClass.candleData import CandleData
|
14
|
-
from tradx.baseClass.touchLineData import TouchLineData
|
15
18
|
|
16
19
|
|
17
20
|
class BaseAlgo(ABC):
|
@@ -71,7 +74,6 @@ class BaseAlgo(ABC):
|
|
71
74
|
self.order_nos = shortuuid
|
72
75
|
self.position_diary: List[Position] = []
|
73
76
|
self.order_diary: List[Order] = []
|
74
|
-
|
75
77
|
# Registering inside interactive engine
|
76
78
|
self.name = interactiveEngine.shortuuid.ShortUUID().random(length=4)
|
77
79
|
self.interactiveEngine.strategy_to_id[self.name] = self
|
@@ -157,7 +159,17 @@ class BaseAlgo(ABC):
|
|
157
159
|
else:
|
158
160
|
# Insert the new order by creating an object
|
159
161
|
new_order = Order(
|
160
|
-
order.OrderUniqueIdentifier,
|
162
|
+
order.OrderUniqueIdentifier,
|
163
|
+
order.AppOrderID,
|
164
|
+
order.ProductType,
|
165
|
+
order.OrderType,
|
166
|
+
order.OrderQuantity,
|
167
|
+
order.OrderDisclosedQuantity,
|
168
|
+
order.OrderPrice,
|
169
|
+
order.OrderStopPrice,
|
170
|
+
order.OrderSide,
|
171
|
+
order.TimeInForce,
|
172
|
+
order.OrderStatus,
|
161
173
|
)
|
162
174
|
self.order_diary.append(new_order)
|
163
175
|
if self.interactiveEngine.user_logger:
|
@@ -305,7 +317,7 @@ class BaseAlgo(ABC):
|
|
305
317
|
|
306
318
|
qty = 0
|
307
319
|
for position in self.position_diary:
|
308
|
-
qty +=
|
320
|
+
qty += position.Quantity
|
309
321
|
return qty != 0
|
310
322
|
|
311
323
|
async def liquidateIntradayDummy(self) -> None:
|
@@ -327,7 +339,7 @@ class BaseAlgo(ABC):
|
|
327
339
|
if position.Quantity != 0:
|
328
340
|
_order_no = self.order_no()
|
329
341
|
asyncio.ensure_future(
|
330
|
-
self.marketDataEngine.
|
342
|
+
self.marketDataEngine.dummy_order(
|
331
343
|
position.ExchangeSegment,
|
332
344
|
position.ExchangeInstrumentID,
|
333
345
|
position.ProductType,
|
@@ -336,3 +348,151 @@ class BaseAlgo(ABC):
|
|
336
348
|
self,
|
337
349
|
)
|
338
350
|
)
|
351
|
+
|
352
|
+
async def safe_market_order(
|
353
|
+
self, ExchangeSegment, ExchangeInstrumentID, ProductType, Quantity
|
354
|
+
):
|
355
|
+
"""
|
356
|
+
Places a safe market order by continuously modifying the order until it is filled.
|
357
|
+
Args:
|
358
|
+
ExchangeSegment (str): The segment of the exchange.
|
359
|
+
ExchangeInstrumentID (int): The instrument ID of the exchange.
|
360
|
+
ProductType (str): The type of the product.
|
361
|
+
Quantity (int): The quantity to be ordered. Positive for buy, negative for sell.
|
362
|
+
Returns:
|
363
|
+
None
|
364
|
+
Raises:
|
365
|
+
Exception: If there is an issue with fetching market data or placing/modifying the order.
|
366
|
+
This method performs the following steps:
|
367
|
+
1. Generates a unique identifier for the order.
|
368
|
+
2. Fetches the latest market data for the given instrument.
|
369
|
+
3. Places a limit order with a slight price adjustment.
|
370
|
+
4. Continuously checks the order status until it is filled.
|
371
|
+
5. If the order is not filled, modifies the order with updated market data.
|
372
|
+
"""
|
373
|
+
OrderUniqueIdentifier = self.order_no()
|
374
|
+
_adjust = Decimal("0.05")
|
375
|
+
Data: TouchLineData = (
|
376
|
+
await self.marketDataEngine.fetch_ltp(
|
377
|
+
[
|
378
|
+
{
|
379
|
+
"exchangeSegment": ExchangeSegment,
|
380
|
+
"exchangeInstrumentID": ExchangeInstrumentID,
|
381
|
+
}
|
382
|
+
]
|
383
|
+
)
|
384
|
+
)[0]
|
385
|
+
|
386
|
+
await self.interactiveEngine.limit_order(
|
387
|
+
ExchangeSegment,
|
388
|
+
ExchangeInstrumentID,
|
389
|
+
ProductType,
|
390
|
+
Quantity,
|
391
|
+
(
|
392
|
+
(Data.AskInfo.Price + _adjust).to_eng_string()
|
393
|
+
if Quantity > 0
|
394
|
+
else (Data.BidInfo.Price - _adjust).to_eng_string()
|
395
|
+
),
|
396
|
+
OrderUniqueIdentifier,
|
397
|
+
)
|
398
|
+
if self.interactiveEngine.user_logger:
|
399
|
+
self.interactiveEngine.user_logger.info(
|
400
|
+
f"Placing Limit Order for {ExchangeInstrumentID} with Quantity {Quantity}",
|
401
|
+
caller=f"{self.__class__.__name__}.safe_market_order",
|
402
|
+
)
|
403
|
+
await asyncio.sleep(0.1)
|
404
|
+
order = next(
|
405
|
+
(
|
406
|
+
O
|
407
|
+
for O in self.order_diary
|
408
|
+
if O.OrderUniqueIdentifier == OrderUniqueIdentifier
|
409
|
+
),
|
410
|
+
None,
|
411
|
+
)
|
412
|
+
while order is None or (order is not None and order.OrderStatus != "Filled"):
|
413
|
+
await asyncio.sleep(0.1)
|
414
|
+
order = next(
|
415
|
+
(
|
416
|
+
O
|
417
|
+
for O in self.order_diary
|
418
|
+
if O.OrderUniqueIdentifier == OrderUniqueIdentifier
|
419
|
+
),
|
420
|
+
None,
|
421
|
+
)
|
422
|
+
if order is not None and order.OrderStatus == "Rejected":
|
423
|
+
if self.interactiveEngine.user_logger:
|
424
|
+
self.interactiveEngine.user_logger.info(
|
425
|
+
f"Order Rejected for {ExchangeInstrumentID} with Quantity {Quantity}",
|
426
|
+
caller=f"{self.__class__.__name__}.safe_market_order",
|
427
|
+
)
|
428
|
+
break
|
429
|
+
if order is not None and order.OrderStatus != "Filled":
|
430
|
+
Data: TouchLineData = (
|
431
|
+
await self.marketDataEngine.fetch_ltp(
|
432
|
+
[
|
433
|
+
{
|
434
|
+
"exchangeSegment": ExchangeSegment,
|
435
|
+
"exchangeInstrumentID": ExchangeInstrumentID,
|
436
|
+
}
|
437
|
+
]
|
438
|
+
)
|
439
|
+
)[0]
|
440
|
+
|
441
|
+
await self.interactiveEngine.xt.modify_order(
|
442
|
+
order.AppOrderID,
|
443
|
+
order.ProductType,
|
444
|
+
order.OrderType,
|
445
|
+
order.OrderQuantity,
|
446
|
+
order.OrderDisclosedQuantity,
|
447
|
+
(
|
448
|
+
(Data.AskInfo.Price + _adjust).to_eng_string()
|
449
|
+
if Quantity > 0
|
450
|
+
else (Data.BidInfo.Price - _adjust).to_eng_string()
|
451
|
+
),
|
452
|
+
order.OrderStopPrice.to_eng_string(),
|
453
|
+
order.TimeInForce,
|
454
|
+
order.OrderUniqueIdentifier,
|
455
|
+
"*****",
|
456
|
+
)
|
457
|
+
if self.interactiveEngine.user_logger:
|
458
|
+
self.interactiveEngine.user_logger.info(
|
459
|
+
f"Modifying Order for {ExchangeInstrumentID} with Quantity {Quantity} and Price "
|
460
|
+
f"{(Data.AskInfo.Price + _adjust).to_eng_string() if Quantity > 0 else (Data.BidInfo.Price - _adjust).to_eng_string()}",
|
461
|
+
caller=f"{self.__class__.__name__}.safe_market_order",
|
462
|
+
)
|
463
|
+
await asyncio.sleep(0.7)
|
464
|
+
|
465
|
+
async def safeLiquidateIntraday(self) -> None:
|
466
|
+
"""
|
467
|
+
Asynchronously liquidates all intraday positions and cancels all open orders.
|
468
|
+
This method performs the following actions:
|
469
|
+
1. Logs the action of canceling open orders and squaring off positions if a user logger is available.
|
470
|
+
2. Iterates through the order diary and cancels any orders with a status of "PendingNew" or "New".
|
471
|
+
3. Iterates through the position diary and places market orders to square off any positions with a non-zero quantity.
|
472
|
+
Returns:
|
473
|
+
None
|
474
|
+
"""
|
475
|
+
|
476
|
+
if self.interactiveEngine.user_logger:
|
477
|
+
self.interactiveEngine.user_logger.info(
|
478
|
+
f"Cancel open order and square off position for strategy {self.name}",
|
479
|
+
caller=f"{self.__class__.__name__}.safeLiquidateIntraday",
|
480
|
+
)
|
481
|
+
|
482
|
+
for order in self.order_diary:
|
483
|
+
if order.OrderStatus in ["PendingNew", "New"]:
|
484
|
+
asyncio.ensure_future(
|
485
|
+
self.interactiveEngine.cancel_order(
|
486
|
+
order.AppOrderID, order.OrderUniqueIdentifier
|
487
|
+
)
|
488
|
+
)
|
489
|
+
for position in self.position_diary:
|
490
|
+
if position.Quantity != 0:
|
491
|
+
asyncio.ensure_future(
|
492
|
+
self.safe_market_order(
|
493
|
+
position.ExchangeSegment,
|
494
|
+
position.ExchangeInstrumentID,
|
495
|
+
position.ProductType,
|
496
|
+
-1 * position.Quantity,
|
497
|
+
)
|
498
|
+
)
|
tradx/baseClass/order.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from pydantic import BaseModel
|
2
|
+
from decimal import Decimal
|
2
3
|
|
3
4
|
|
4
5
|
class Order(BaseModel):
|
@@ -16,12 +17,39 @@ class Order(BaseModel):
|
|
16
17
|
OrderUniqueIdentifier: str
|
17
18
|
AppOrderID: int
|
18
19
|
OrderStatus: str
|
20
|
+
ProductType: str
|
21
|
+
OrderType: str
|
22
|
+
OrderQuantity: int
|
23
|
+
OrderDisclosedQuantity: int
|
24
|
+
OrderPrice: Decimal
|
25
|
+
OrderStopPrice: Decimal
|
26
|
+
TimeInForce: str
|
27
|
+
OrderSide: str
|
19
28
|
|
20
29
|
def __init__(
|
21
|
-
self,
|
30
|
+
self,
|
31
|
+
OrderUniqueIdentifier: str,
|
32
|
+
AppOrderID: int,
|
33
|
+
ProductType: str,
|
34
|
+
OrderType: str,
|
35
|
+
OrderQuantity: int,
|
36
|
+
OrderDisclosedQuantity: int,
|
37
|
+
OrderPrice: Decimal,
|
38
|
+
OrderStopPrice: Decimal,
|
39
|
+
OrderSide: str,
|
40
|
+
TimeInForce: str,
|
41
|
+
OrderStatus: str = "",
|
22
42
|
):
|
23
43
|
super().__init__(
|
24
44
|
OrderUniqueIdentifier=OrderUniqueIdentifier,
|
25
45
|
AppOrderID=AppOrderID,
|
46
|
+
ProductType=ProductType,
|
47
|
+
OrderType=OrderType,
|
48
|
+
OrderQuantity=OrderQuantity,
|
49
|
+
OrderDisclosedQuantity=OrderDisclosedQuantity,
|
50
|
+
OrderPrice=OrderPrice,
|
51
|
+
OrderStopPrice=OrderStopPrice,
|
26
52
|
OrderStatus=OrderStatus,
|
53
|
+
OrderSide=OrderSide,
|
54
|
+
TimeInForce=TimeInForce,
|
27
55
|
)
|
tradx/marketDataEngine.py
CHANGED
@@ -646,16 +646,19 @@ class marketDataEngine(MarketDataSocketClient):
|
|
646
646
|
baseAlgo: BaseAlgo,
|
647
647
|
):
|
648
648
|
"""
|
649
|
-
Simulates a market order
|
649
|
+
Simulates a market order and generates a trade event.
|
650
650
|
Args:
|
651
|
-
exchangeSegment (str): The
|
652
|
-
exchangeInstrumentID (int): The
|
653
|
-
productType (str): The product
|
651
|
+
exchangeSegment (str): The segment of the exchange where the order is placed.
|
652
|
+
exchangeInstrumentID (int): The ID of the instrument being traded.
|
653
|
+
productType (str): The type of product being traded.
|
654
654
|
orderQuantity (int): The quantity of the order. Positive for buy, negative for sell.
|
655
655
|
orderUniqueIdentifier (str): A unique identifier for the order.
|
656
|
-
baseAlgo (BaseAlgo): An instance of
|
656
|
+
baseAlgo (BaseAlgo): An instance of the BaseAlgo class to handle the trade event.
|
657
657
|
Returns:
|
658
658
|
None
|
659
|
+
This function fetches the last traded price (LTP) for the given instrument and creates a TradeEvent
|
660
|
+
based on whether the order is a buy or sell. The TradeEvent is then passed to the baseAlgo's trade_
|
661
|
+
method for further processing.
|
659
662
|
"""
|
660
663
|
_list = await self.fetch_ltp(
|
661
664
|
[
|
@@ -691,7 +694,7 @@ class marketDataEngine(MarketDataSocketClient):
|
|
691
694
|
"OrderQuantity": abs(orderQuantity),
|
692
695
|
"OrderStopPrice": 0,
|
693
696
|
"OrderStatus": "Filled",
|
694
|
-
"OrderAverageTradedPrice": _data.
|
697
|
+
"OrderAverageTradedPrice": _data.LastTradedPrice,
|
695
698
|
"LeavesQuantity": 0,
|
696
699
|
"CumulativeQuantity": abs(orderQuantity),
|
697
700
|
"OrderDisclosedQuantity": 0,
|
@@ -701,13 +704,13 @@ class marketDataEngine(MarketDataSocketClient):
|
|
701
704
|
"CancelRejectReason": "",
|
702
705
|
"OrderUniqueIdentifier": orderUniqueIdentifier,
|
703
706
|
"OrderLegStatus": "SingleOrderLeg",
|
704
|
-
"LastTradedPrice": _data.
|
707
|
+
"LastTradedPrice": _data.LastTradedPrice,
|
705
708
|
"LastTradedQuantity": 0,
|
706
709
|
"LastExecutionTransactTime": "2025-01-06T10:14:40",
|
707
710
|
"ExecutionID": "402597456",
|
708
711
|
"ExecutionReportIndex": 4,
|
709
712
|
"IsSpread": False,
|
710
|
-
"OrderAverageTradedPriceAPI": _data.
|
713
|
+
"OrderAverageTradedPriceAPI": _data.LastTradedPrice,
|
711
714
|
"OrderSideAPI": "SELL",
|
712
715
|
"OrderGeneratedDateTimeAPI": datetime.now(),
|
713
716
|
"ExchangeTransactTimeAPI": datetime.now(),
|
@@ -743,7 +746,7 @@ class marketDataEngine(MarketDataSocketClient):
|
|
743
746
|
"OrderQuantity": orderQuantity,
|
744
747
|
"OrderStopPrice": 0,
|
745
748
|
"OrderStatus": "Filled",
|
746
|
-
"OrderAverageTradedPrice": _data.
|
749
|
+
"OrderAverageTradedPrice": _data.LastTradedPrice,
|
747
750
|
"LeavesQuantity": 0,
|
748
751
|
"CumulativeQuantity": orderQuantity,
|
749
752
|
"OrderDisclosedQuantity": 0,
|
@@ -753,13 +756,13 @@ class marketDataEngine(MarketDataSocketClient):
|
|
753
756
|
"CancelRejectReason": "",
|
754
757
|
"OrderUniqueIdentifier": orderUniqueIdentifier,
|
755
758
|
"OrderLegStatus": "SingleOrderLeg",
|
756
|
-
"LastTradedPrice": _data.
|
759
|
+
"LastTradedPrice": _data.LastTradedPrice,
|
757
760
|
"LastTradedQuantity": 0,
|
758
761
|
"LastExecutionTransactTime": "2025-01-15T15:09:56",
|
759
762
|
"ExecutionID": "409661490",
|
760
763
|
"ExecutionReportIndex": 3,
|
761
764
|
"IsSpread": False,
|
762
|
-
"OrderAverageTradedPriceAPI": _data.
|
765
|
+
"OrderAverageTradedPriceAPI": _data.LastTradedPrice,
|
763
766
|
"OrderSideAPI": "BUY",
|
764
767
|
"OrderGeneratedDateTimeAPI": "15-01-2025 15:10:00",
|
765
768
|
"ExchangeTransactTimeAPI": "15-01-2025 15:09:56",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: tradx
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.3
|
4
4
|
Summary: A Package Designed to simplify strategy development on package xts-api-client
|
5
5
|
Author-email: "jatin.kumawat" <jatin.kumawat@rmoneyindia.com>
|
6
6
|
Requires-Python: >=3.12
|
@@ -8,7 +8,7 @@ Requires-Dist: aiohttp==3.11.11
|
|
8
8
|
Requires-Dist: pydantic==2.10.4
|
9
9
|
Requires-Dist: pytest==8.3.4
|
10
10
|
Requires-Dist: shortuuid==1.0.13
|
11
|
-
Requires-Dist: xts-api-client
|
11
|
+
Requires-Dist: xts-api-client>=0.1.7
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
|
14
14
|
# Changelog
|
@@ -2,9 +2,9 @@ tradx/__init__.py,sha256=MlWuula4lJZLPYPi4d5ZE9yoJnYWtgbZ0QsgWdWPwU0,53
|
|
2
2
|
tradx/algoContainer.py,sha256=1IkVCIF_gXIby8z3pDdlVeUablh-PZVZ1EawyCB7oUs,3807
|
3
3
|
tradx/dualHashMap.py,sha256=XsidIc3aMvpVGOvdfV7lOeZaLCWAD5i180BGyAfdYXE,1737
|
4
4
|
tradx/interactiveEngine.py,sha256=DCuEEYJcTn6DEG9pbaZsmGyHeyWGNG2q46Iljel6Drc,34584
|
5
|
-
tradx/marketDataEngine.py,sha256=
|
5
|
+
tradx/marketDataEngine.py,sha256=pIY4dephqzykzPYJk0khYMVmD1QAr-17CtLN1WkcAWM,34597
|
6
6
|
tradx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
tradx/baseClass/baseAlgo.py,sha256=
|
7
|
+
tradx/baseClass/baseAlgo.py,sha256=AoguFsuAwJwnfWL20bnp3eZP92JXskrLDYIoENCbJ6c,20639
|
8
8
|
tradx/baseClass/candleData.py,sha256=tS-iAoRGwK2xVSvrqmNZPYeB63qD53oPPHaUDfJBWkk,2947
|
9
9
|
tradx/baseClass/cmInstrument.py,sha256=WpibHdJyVGVs0B8o1COiXr4rNXB8bapzFLyRaIG0w9Q,2408
|
10
10
|
tradx/baseClass/futureInstrument.py,sha256=ygsGfzUg337gSwoSaAb8DB31YhLOI-nOv3ApX3z5CWQ,2186
|
@@ -17,7 +17,7 @@ tradx/baseClass/marketStatusData.py,sha256=lZKJlYB_Bfc1Rpv4rQ15ZQTXgH5EkBDOvH6RS
|
|
17
17
|
tradx/baseClass/openInterestData.py,sha256=L-WuxyNwdZAFPoSVhfJPKv8jOy5K0rSB2o5EkWVSv9g,3111
|
18
18
|
tradx/baseClass/openInterestPartialData.py,sha256=vpo18P9VCBuCXbxjggg4ahd0uS5WkP14rBvcLwdrTFw,1673
|
19
19
|
tradx/baseClass/optionsInstrument.py,sha256=O3zhNf1R1wmtdPCUaDr7mOM7co0Aeg8AHmPvVpabP60,9886
|
20
|
-
tradx/baseClass/order.py,sha256=
|
20
|
+
tradx/baseClass/order.py,sha256=1o3AtlysNBs4dRwSjCh11AOoJ97QCNra4D9HLv9vMm4,1699
|
21
21
|
tradx/baseClass/orderEvent.py,sha256=P4sJW3NKi53JDo8RwMVVKpCA_dIpkcE1-sm9ikpFYWk,2901
|
22
22
|
tradx/baseClass/position.py,sha256=6fzm_Mr1GKSaRArRgsZItgw74_8omy-MBXRTfO12Pwk,1551
|
23
23
|
tradx/baseClass/positionEvent.py,sha256=odOMeBqKUKfJ9Zj7IPcipMyfRTMsQyA-hvTJ_NcqKUk,3484
|
@@ -28,6 +28,6 @@ tradx/baseClass/tradeEvent.py,sha256=djunJW5AzjeMfJZVMlrFprplB7vrYBi-mmaR1TA0MK4
|
|
28
28
|
tradx/constants/holidays.py,sha256=GCg0xGvXm1EM0n6YF1KYEnldSiC2sbsc09Iekjwn0ww,1547
|
29
29
|
tradx/logger/logger.py,sha256=DfrjzwYkujTq7arksNTPcQeioXnwT1xgN659blhreog,3232
|
30
30
|
tradx/logger/logger2.py,sha256=ebJ-qqnpnCqvyx1Cz1-kGGULtkH-hfrK6UNfa0bSlH8,2654
|
31
|
-
tradx-0.1.
|
32
|
-
tradx-0.1.
|
33
|
-
tradx-0.1.
|
31
|
+
tradx-0.1.3.dist-info/METADATA,sha256=KGAGYRn_xFNqUR5t0v8wRn6kMZo3iHWYLEaRCvwxqUo,2627
|
32
|
+
tradx-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
33
|
+
tradx-0.1.3.dist-info/RECORD,,
|
File without changes
|