tradx 0.8.3__py3-none-any.whl → 0.9.1__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/algoContainer.py +82 -2
- tradx/baseClass/baseAlgo.py +24 -9
- tradx/baseClass/{order.py → interactive/order.py} +6 -0
- tradx/baseClass/{orderEvent.py → interactive/orderEvent.py} +3 -1
- tradx/baseClass/{position.py → interactive/position.py} +8 -1
- tradx/interactiveEngine.py +197 -58
- tradx/marketDataEngine.py +254 -76
- {tradx-0.8.3.dist-info → tradx-0.9.1.dist-info}/METADATA +1 -1
- tradx-0.9.1.dist-info/RECORD +33 -0
- tradx-0.8.3.dist-info/RECORD +0 -33
- /tradx/baseClass/{positionEvent.py → interactive/positionEvent.py} +0 -0
- /tradx/baseClass/{tradeConversionEvent.py → interactive/tradeConversionEvent.py} +0 -0
- /tradx/baseClass/{tradeEvent.py → interactive/tradeEvent.py} +0 -0
- /tradx/baseClass/{candleData.py → market/candleData.py} +0 -0
- /tradx/baseClass/{cmInstrument.py → market/cmInstrument.py} +0 -0
- /tradx/baseClass/{futureInstrument.py → market/futureInstrument.py} +0 -0
- /tradx/baseClass/{index.py → market/index.py} +0 -0
- /tradx/baseClass/{instrumentPropertyChangeData.py → market/instrumentPropertyChangeData.py} +0 -0
- /tradx/baseClass/{ltpData.py → market/ltpData.py} +0 -0
- /tradx/baseClass/{ltpPartialData.py → market/ltpPartialData.py} +0 -0
- /tradx/baseClass/{marketDepthData.py → market/marketDepthData.py} +0 -0
- /tradx/baseClass/{marketStatusData.py → market/marketStatusData.py} +0 -0
- /tradx/baseClass/{openInterestData.py → market/openInterestData.py} +0 -0
- /tradx/baseClass/{openInterestPartialData.py → market/openInterestPartialData.py} +0 -0
- /tradx/baseClass/{optionsInstrument.py → market/optionsInstrument.py} +0 -0
- /tradx/baseClass/{touchLineData.py → market/touchLineData.py} +0 -0
- /tradx/baseClass/{touchLinePartialData.py → market/touchLinePartialData.py} +0 -0
- {tradx-0.8.3.dist-info → tradx-0.9.1.dist-info}/WHEEL +0 -0
tradx/algoContainer.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from typing import Dict, Set, Any
|
2
2
|
from tradx.baseClass.baseAlgo import BaseAlgo
|
3
|
-
from tradx.baseClass.candleData import CandleData
|
4
|
-
from tradx.baseClass.touchLineData import TouchLineData
|
3
|
+
from tradx.baseClass.market.candleData import CandleData
|
4
|
+
from tradx.baseClass.market.touchLineData import TouchLineData
|
5
5
|
import asyncio
|
6
6
|
|
7
7
|
|
@@ -88,3 +88,83 @@ class AlgoContainer:
|
|
88
88
|
if isinstance(message, TouchLineData):
|
89
89
|
for algo in self.subscriptions[message.ExchangeInstrumentID]:
|
90
90
|
asyncio.ensure_future(algo.on_touchLineData(message))
|
91
|
+
|
92
|
+
|
93
|
+
class SubcribeContainer:
|
94
|
+
"""
|
95
|
+
AlgoContainer is a container class that maps algorithms to their subscriptions and allows broadcasting messages.
|
96
|
+
Attributes:
|
97
|
+
subscriptions (Dict[int, Set[BaseAlgo]]): A dictionary mapping subscription keys to sets of algorithm instances.
|
98
|
+
Methods:
|
99
|
+
__init__():
|
100
|
+
Initializes the AlgoContainer with an empty subscription map.
|
101
|
+
subscribe(key: int, algo: BaseAlgo):
|
102
|
+
Subscribes an algorithm to a specific key.
|
103
|
+
key (int): The subscription key.
|
104
|
+
algo (BaseAlgo): The algorithm instance subscribing.
|
105
|
+
unsubscribe(key: int, algo: BaseAlgo):
|
106
|
+
Unsubscribes an algorithm from a specific key.
|
107
|
+
key (int): The subscription key.
|
108
|
+
algo (BaseAlgo): The algorithm instance unsubscribing.
|
109
|
+
broadcast(key: int, message: str, dataType: str):
|
110
|
+
Broadcasts a message to all algorithms subscribed to a given key.
|
111
|
+
key (int): The key to broadcast the message for.
|
112
|
+
dataType (str): The type of data being broadcasted (e.g., "bar").
|
113
|
+
"""
|
114
|
+
|
115
|
+
def __init__(self) -> None:
|
116
|
+
"""
|
117
|
+
Initializes the algoContainer instance.
|
118
|
+
|
119
|
+
Attributes:
|
120
|
+
subscriptions (Dict[int, Set[BaseAlgo]]):
|
121
|
+
A dictionary mapping an integer key (e.g., a topic or identifier)
|
122
|
+
to a set of algorithm instances (BaseAlgo).
|
123
|
+
"""
|
124
|
+
self.subscriptions: Dict[int, Set[str]] = {}
|
125
|
+
|
126
|
+
def subscribe(self, exchangeInstrumentID, xtsMessageCode, uuid: str) -> None:
|
127
|
+
"""
|
128
|
+
Subscribes an algorithm to a given key.
|
129
|
+
Args:
|
130
|
+
key: The key to subscribe the algorithm to.
|
131
|
+
algo (BaseAlgo): The algorithm to be subscribed.
|
132
|
+
Raises:
|
133
|
+
None
|
134
|
+
Returns:
|
135
|
+
None
|
136
|
+
"""
|
137
|
+
key = (exchangeInstrumentID, xtsMessageCode)
|
138
|
+
if key not in self.subscriptions:
|
139
|
+
self.subscriptions[key] = set()
|
140
|
+
self.subscriptions[key].add(uuid)
|
141
|
+
|
142
|
+
def unsubscribe(self, exchangeInstrumentID, xtsMessageCode, uuid: str) -> None:
|
143
|
+
"""
|
144
|
+
Unsubscribe an algorithm from a given key.
|
145
|
+
This method removes the specified algorithm from the list of
|
146
|
+
subscriptions associated with the given key. If no more algorithms
|
147
|
+
are subscribed to the key after removal, the key is deleted from
|
148
|
+
the subscriptions.
|
149
|
+
Args:
|
150
|
+
key: The key from which the algorithm should be unsubscribed.
|
151
|
+
algo (BaseAlgo): The algorithm instance to be unsubscribed.
|
152
|
+
"""
|
153
|
+
key = (exchangeInstrumentID, xtsMessageCode)
|
154
|
+
if key in self.subscriptions and uuid in self.subscriptions[key]:
|
155
|
+
self.subscriptions[key].remove(uuid)
|
156
|
+
|
157
|
+
# Remove the key if no more algorithms are subscribed
|
158
|
+
if not self.subscriptions[key]:
|
159
|
+
del self.subscriptions[key]
|
160
|
+
|
161
|
+
def ifExists(self, exchangeInstrumentID, xtsMessageCode) -> bool:
|
162
|
+
"""
|
163
|
+
Check if a subscription exists for the given exchangeInstrumentID and xtsMessageCode.
|
164
|
+
Args:
|
165
|
+
exchangeInstrumentID: The exchange instrument ID to check.
|
166
|
+
xtsMessageCode: The XTS message code to check.
|
167
|
+
Returns:
|
168
|
+
bool: True if the subscription exists, False otherwise.
|
169
|
+
"""
|
170
|
+
return (exchangeInstrumentID, xtsMessageCode) in self.subscriptions
|
tradx/baseClass/baseAlgo.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
2
|
import shortuuid
|
3
|
-
from typing import Any, TYPE_CHECKING, List, Dict
|
3
|
+
from typing import Any, TYPE_CHECKING, List, Dict, Set
|
4
4
|
from decimal import Decimal
|
5
|
-
from tradx.baseClass.order import Order
|
6
|
-
from tradx.baseClass.position import Position
|
5
|
+
from tradx.baseClass.interactive.order import Order
|
6
|
+
from tradx.baseClass.interactive.position import Position
|
7
7
|
|
8
8
|
import asyncio
|
9
9
|
|
@@ -11,10 +11,10 @@ import asyncio
|
|
11
11
|
if TYPE_CHECKING:
|
12
12
|
from tradx.marketDataEngine import marketDataEngine
|
13
13
|
from tradx.interactiveEngine import interactiveEngine
|
14
|
-
from tradx.baseClass.touchLineData import TouchLineData
|
15
|
-
from tradx.baseClass.orderEvent import OrderEvent
|
16
|
-
from tradx.baseClass.tradeEvent import TradeEvent
|
17
|
-
from tradx.baseClass.candleData import CandleData
|
14
|
+
from tradx.baseClass.market.touchLineData import TouchLineData
|
15
|
+
from tradx.baseClass.interactive.orderEvent import OrderEvent
|
16
|
+
from tradx.baseClass.interactive.tradeEvent import TradeEvent
|
17
|
+
from tradx.baseClass.market.candleData import CandleData
|
18
18
|
|
19
19
|
|
20
20
|
class BaseAlgo(ABC):
|
@@ -74,6 +74,7 @@ class BaseAlgo(ABC):
|
|
74
74
|
self.order_nos = shortuuid
|
75
75
|
self.position_diary: List[Position] = []
|
76
76
|
self.order_diary: List[Order] = []
|
77
|
+
self.uuids = set()
|
77
78
|
# Registering inside interactive engine
|
78
79
|
self.name = interactiveEngine.shortuuid.ShortUUID().random(length=4)
|
79
80
|
self.interactiveEngine.strategy_to_id[self.name] = self
|
@@ -127,7 +128,13 @@ class BaseAlgo(ABC):
|
|
127
128
|
Returns:
|
128
129
|
str: An 8-digit unique order identifier.
|
129
130
|
"""
|
130
|
-
|
131
|
+
# Generate a unique 4-character key not present in self.uuids
|
132
|
+
while True:
|
133
|
+
ids = self.order_nos.ShortUUID().random(length=4)
|
134
|
+
if ids not in self.uuids:
|
135
|
+
self.uuids.add(ids)
|
136
|
+
break
|
137
|
+
return f"{self.name}{ids}"
|
131
138
|
|
132
139
|
async def order_(self, order: "OrderEvent") -> None:
|
133
140
|
"""
|
@@ -152,6 +159,14 @@ class BaseAlgo(ABC):
|
|
152
159
|
# Update the status of the existing order
|
153
160
|
existing_order.OrderStatus = order.OrderStatus
|
154
161
|
existing_order.OrderPrice = order.OrderAverageTradedPriceAPI
|
162
|
+
if existing_order.AppOrderID != order.AppOrderID:
|
163
|
+
|
164
|
+
if self.interactiveEngine.user_logger:
|
165
|
+
self.interactiveEngine.user_logger.info(
|
166
|
+
f"Updated order {order.OrderUniqueIdentifier} App order id from {existing_order.AppOrderID} to {order.AppOrderID}",
|
167
|
+
caller=f"{self.__class__.__name__}.order_",
|
168
|
+
)
|
169
|
+
existing_order.AppOrderID = order.AppOrderID
|
155
170
|
if self.interactiveEngine.user_logger:
|
156
171
|
self.interactiveEngine.user_logger.info(
|
157
172
|
f"Updated order {order.OrderUniqueIdentifier} status to {order.OrderStatus}",
|
@@ -289,7 +304,7 @@ class BaseAlgo(ABC):
|
|
289
304
|
)
|
290
305
|
|
291
306
|
for order in self.order_diary:
|
292
|
-
if order.OrderStatus in ["PendingNew", "New"]:
|
307
|
+
if order.OrderStatus in ["PendingNew", "New", "Replaced", "PendingReplace"]:
|
293
308
|
asyncio.ensure_future(
|
294
309
|
self.interactiveEngine.cancel_order(
|
295
310
|
order.AppOrderID, order.OrderUniqueIdentifier
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from pydantic import BaseModel
|
2
2
|
from decimal import Decimal
|
3
|
+
import json
|
3
4
|
|
4
5
|
|
5
6
|
class Order(BaseModel):
|
@@ -53,3 +54,8 @@ class Order(BaseModel):
|
|
53
54
|
OrderSide=OrderSide,
|
54
55
|
TimeInForce=TimeInForce,
|
55
56
|
)
|
57
|
+
|
58
|
+
@classmethod
|
59
|
+
def from_str(cls, message: str):
|
60
|
+
data = json.loads(message)
|
61
|
+
return cls(**data)
|
@@ -34,7 +34,9 @@ def parse_datetime(value) -> Any:
|
|
34
34
|
return datetime(1980, 1, 1) + timedelta(seconds=value)
|
35
35
|
return value
|
36
36
|
class OrderEvent(BaseModel):
|
37
|
-
|
37
|
+
"""
|
38
|
+
Order Event class representing an order event in the system.
|
39
|
+
"""
|
38
40
|
|
39
41
|
LoginID: str
|
40
42
|
ClientID: str = "*****"
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from pydantic import BaseModel
|
2
|
+
import json
|
2
3
|
|
3
4
|
|
4
5
|
class Position(BaseModel):
|
@@ -20,8 +21,9 @@ class Position(BaseModel):
|
|
20
21
|
Constructs all the necessary attributes for the Position object.
|
21
22
|
"""
|
22
23
|
|
23
|
-
ExchangeSegment: str
|
24
24
|
ExchangeInstrumentID: int
|
25
|
+
ExchangeSegment: str
|
26
|
+
|
25
27
|
ProductType: str
|
26
28
|
Quantity: int
|
27
29
|
|
@@ -44,3 +46,8 @@ class Position(BaseModel):
|
|
44
46
|
ProductType=ProductType,
|
45
47
|
Quantity=Quantity,
|
46
48
|
)
|
49
|
+
|
50
|
+
@classmethod
|
51
|
+
def from_str(cls, message: str):
|
52
|
+
data = json.loads(message)
|
53
|
+
return cls(**data)
|