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
tradx/__init__.py
ADDED
tradx/algoContainer.py
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
from typing import Dict, Set, Any
|
2
|
+
from tradx.baseClass.baseAlgo import BaseAlgo
|
3
|
+
from tradx.baseClass.candleData import CandleData
|
4
|
+
from tradx.baseClass.touchLineData import TouchLineData
|
5
|
+
import asyncio
|
6
|
+
|
7
|
+
|
8
|
+
class AlgoContainer:
|
9
|
+
"""
|
10
|
+
AlgoContainer is a container class that maps algorithms to their subscriptions and allows broadcasting messages.
|
11
|
+
Attributes:
|
12
|
+
subscriptions (Dict[int, Set[BaseAlgo]]): A dictionary mapping subscription keys to sets of algorithm instances.
|
13
|
+
Methods:
|
14
|
+
__init__():
|
15
|
+
Initializes the AlgoContainer with an empty subscription map.
|
16
|
+
subscribe(key: int, algo: BaseAlgo):
|
17
|
+
Subscribes an algorithm to a specific key.
|
18
|
+
key (int): The subscription key.
|
19
|
+
algo (BaseAlgo): The algorithm instance subscribing.
|
20
|
+
unsubscribe(key: int, algo: BaseAlgo):
|
21
|
+
Unsubscribes an algorithm from a specific key.
|
22
|
+
key (int): The subscription key.
|
23
|
+
algo (BaseAlgo): The algorithm instance unsubscribing.
|
24
|
+
broadcast(key: int, message: str, dataType: str):
|
25
|
+
Broadcasts a message to all algorithms subscribed to a given key.
|
26
|
+
key (int): The key to broadcast the message for.
|
27
|
+
dataType (str): The type of data being broadcasted (e.g., "bar").
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self) -> None:
|
31
|
+
"""
|
32
|
+
Initializes the algoContainer instance.
|
33
|
+
|
34
|
+
Attributes:
|
35
|
+
subscriptions (Dict[int, Set[BaseAlgo]]):
|
36
|
+
A dictionary mapping an integer key (e.g., a topic or identifier)
|
37
|
+
to a set of algorithm instances (BaseAlgo).
|
38
|
+
"""
|
39
|
+
self.subscriptions: Dict[int, Set[BaseAlgo]] = {}
|
40
|
+
|
41
|
+
def subscribe(self, key, algo: BaseAlgo) -> None:
|
42
|
+
"""
|
43
|
+
Subscribes an algorithm to a given key.
|
44
|
+
Args:
|
45
|
+
key: The key to subscribe the algorithm to.
|
46
|
+
algo (BaseAlgo): The algorithm to be subscribed.
|
47
|
+
Raises:
|
48
|
+
None
|
49
|
+
Returns:
|
50
|
+
None
|
51
|
+
"""
|
52
|
+
|
53
|
+
if key not in self.subscriptions:
|
54
|
+
self.subscriptions[key] = set()
|
55
|
+
self.subscriptions[key].add(algo)
|
56
|
+
|
57
|
+
def unsubscribe(self, key, algo: BaseAlgo) -> None:
|
58
|
+
"""
|
59
|
+
Unsubscribe an algorithm from a given key.
|
60
|
+
This method removes the specified algorithm from the list of
|
61
|
+
subscriptions associated with the given key. If no more algorithms
|
62
|
+
are subscribed to the key after removal, the key is deleted from
|
63
|
+
the subscriptions.
|
64
|
+
Args:
|
65
|
+
key: The key from which the algorithm should be unsubscribed.
|
66
|
+
algo (BaseAlgo): The algorithm instance to be unsubscribed.
|
67
|
+
"""
|
68
|
+
|
69
|
+
if key in self.subscriptions and algo in self.subscriptions[key]:
|
70
|
+
self.subscriptions[key].remove(algo)
|
71
|
+
|
72
|
+
# Remove the key if no more algorithms are subscribed
|
73
|
+
if not self.subscriptions[key]:
|
74
|
+
del self.subscriptions[key]
|
75
|
+
|
76
|
+
async def broadcast(self, message: Any) -> None:
|
77
|
+
"""
|
78
|
+
Broadcast a message to all algorithms subscribed to a given key.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
key (str): The key to broadcast the message for.
|
82
|
+
message (str): The message to broadcast.
|
83
|
+
"""
|
84
|
+
if message.ExchangeInstrumentID in self.subscriptions:
|
85
|
+
if isinstance(message, CandleData):
|
86
|
+
for algo in self.subscriptions[message.ExchangeInstrumentID]:
|
87
|
+
asyncio.ensure_future(algo.on_barData(message))
|
88
|
+
if isinstance(message, TouchLineData):
|
89
|
+
for algo in self.subscriptions[message.ExchangeInstrumentID]:
|
90
|
+
asyncio.ensure_future(algo.on_touchLineData(message))
|
@@ -0,0 +1,338 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
import shortuuid
|
3
|
+
from typing import Any, TYPE_CHECKING, List
|
4
|
+
from tradx.baseClass.order import Order
|
5
|
+
from tradx.baseClass.position import Position
|
6
|
+
import asyncio
|
7
|
+
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from tradx import interactiveEngine, marketDataEngine
|
11
|
+
from tradx.baseClass.orderEvent import OrderEvent
|
12
|
+
from tradx.baseClass.tradeEvent import TradeEvent
|
13
|
+
from tradx.baseClass.candleData import CandleData
|
14
|
+
from tradx.baseClass.touchLineData import TouchLineData
|
15
|
+
|
16
|
+
|
17
|
+
class BaseAlgo(ABC):
|
18
|
+
"""
|
19
|
+
BaseAlgo is an abstract base class for algorithmic trading strategies. It provides a framework for handling market data, orders, and trades, and requires subclasses to implement specific methods for processing data and events.
|
20
|
+
Attributes:
|
21
|
+
cache (dict): A cache for storing temporary data.
|
22
|
+
order_nos (ShortUUID): Instance of ShortUUID for generating unique order identifiers.
|
23
|
+
position_diary (List[Position]): A list to store position data.
|
24
|
+
order_diary (List[Order]): A list to store order data.
|
25
|
+
name (str): A unique identifier for the algorithm.
|
26
|
+
Methods:
|
27
|
+
__init__(marketDataEngine, interactiveEngine):
|
28
|
+
Initializes the BaseAlgo class with marketDataEngine and interactiveEngine instances.
|
29
|
+
on_barData(data):
|
30
|
+
Abstract method to process candle data. Must be implemented by subclasses.
|
31
|
+
subscribe():
|
32
|
+
Abstract method to handle subscription logic. Must be implemented by subclasses.
|
33
|
+
order_no():
|
34
|
+
Generates a unique order identifier.
|
35
|
+
order_(order):
|
36
|
+
Handles the order event by updating the status of an existing order or inserting a new order.
|
37
|
+
on_orderEvent(message):
|
38
|
+
Abstract method to handle order events. Must be implemented by subclasses.
|
39
|
+
trade_(trade):
|
40
|
+
Handles a trade event by updating the position diary and triggering the on_tradeEvent callback.
|
41
|
+
on_tradeEvent(message):
|
42
|
+
Abstract method to handle trade events. Must be implemented by subclasses.
|
43
|
+
unsubscribe():
|
44
|
+
Abstract method to handle unsubscription logic. Must be implemented by subclasses.
|
45
|
+
initialize():
|
46
|
+
Abstract method to initialize the strategy. Must be implemented by subclasses.
|
47
|
+
deinitialize():
|
48
|
+
Abstract method to deinitialize the strategy. Must be implemented by subclasses.
|
49
|
+
liquidateIntraday():
|
50
|
+
Class method to liquidate all intraday positions and cancel pending orders.
|
51
|
+
isInvested():
|
52
|
+
Class method to check if there are any investments by summing the quantities of all positions in the position diary.
|
53
|
+
"""
|
54
|
+
|
55
|
+
def __init__(
|
56
|
+
self,
|
57
|
+
marketDataEngine: "marketDataEngine",
|
58
|
+
interactiveEngine: "interactiveEngine",
|
59
|
+
):
|
60
|
+
"""
|
61
|
+
Initialize the BaseAlgo class.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
marketDataEngine (marketDataEngine): Instance of marketDataEngine.
|
65
|
+
interactiveEngine (interactiveEngine): Instance of interactiveEngine.
|
66
|
+
"""
|
67
|
+
# Initialize instance variables
|
68
|
+
self.cache = {}
|
69
|
+
self.marketDataEngine = marketDataEngine
|
70
|
+
self.interactiveEngine = interactiveEngine
|
71
|
+
self.order_nos = shortuuid
|
72
|
+
self.position_diary: List[Position] = []
|
73
|
+
self.order_diary: List[Order] = []
|
74
|
+
|
75
|
+
# Registering inside interactive engine
|
76
|
+
self.name = interactiveEngine.shortuuid.ShortUUID().random(length=4)
|
77
|
+
self.interactiveEngine.strategy_to_id[self.name] = self
|
78
|
+
|
79
|
+
# Logging the initialization
|
80
|
+
if self.marketDataEngine.user_logger:
|
81
|
+
self.marketDataEngine.user_logger.info(
|
82
|
+
f"Algorithm initialized with ID: {self.name}",
|
83
|
+
caller=f"{self.__class__.__name__}.__init__",
|
84
|
+
)
|
85
|
+
|
86
|
+
@abstractmethod
|
87
|
+
async def on_barData(self, data: "CandleData") -> None:
|
88
|
+
"""
|
89
|
+
Abstract method (virtual function) to process candle data.
|
90
|
+
Must be implemented by subclasses.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
data (any): The input data to process.
|
94
|
+
"""
|
95
|
+
...
|
96
|
+
|
97
|
+
@abstractmethod
|
98
|
+
async def on_touchLineData(self, data: "TouchLineData") -> None:
|
99
|
+
"""
|
100
|
+
Asynchronous method to handle touch line data.
|
101
|
+
Args:
|
102
|
+
data (TouchLineData): The touch line data to be processed.
|
103
|
+
Returns:
|
104
|
+
None
|
105
|
+
"""
|
106
|
+
|
107
|
+
...
|
108
|
+
|
109
|
+
@abstractmethod
|
110
|
+
async def subscribe(self) -> None:
|
111
|
+
"""
|
112
|
+
Abstract method (virtual function) to process subscribe.
|
113
|
+
Must be implemented by subclasses.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
data (any): The input data to process.
|
117
|
+
"""
|
118
|
+
...
|
119
|
+
|
120
|
+
def order_no(self) -> str:
|
121
|
+
"""
|
122
|
+
Generate a unique order identifier.
|
123
|
+
The identifier is an 8-digit string composed of a 4-digit algorithm ID (derived from `self.name`)
|
124
|
+
and a 4-digit random sequence generated using the `ShortUUID` library.
|
125
|
+
Returns:
|
126
|
+
str: An 8-digit unique order identifier.
|
127
|
+
"""
|
128
|
+
return f"{self.name}{self.order_nos.ShortUUID().random(length=4)}"
|
129
|
+
|
130
|
+
async def order_(self, order: "OrderEvent") -> None:
|
131
|
+
"""
|
132
|
+
Handles the order event by either updating the status of an existing order
|
133
|
+
or inserting a new order into the order diary.
|
134
|
+
Args:
|
135
|
+
order (OrderEvent): The order event containing order details.
|
136
|
+
Returns:
|
137
|
+
None
|
138
|
+
"""
|
139
|
+
|
140
|
+
existing_order = next(
|
141
|
+
(
|
142
|
+
O
|
143
|
+
for O in self.order_diary
|
144
|
+
if O.OrderUniqueIdentifier == order.OrderUniqueIdentifier
|
145
|
+
),
|
146
|
+
None,
|
147
|
+
)
|
148
|
+
|
149
|
+
if existing_order:
|
150
|
+
# Update the status of the existing order
|
151
|
+
existing_order.OrderStatus = order.OrderStatus
|
152
|
+
if self.interactiveEngine.user_logger:
|
153
|
+
self.interactiveEngine.user_logger.info(
|
154
|
+
f"Updated order {order.OrderUniqueIdentifier} status to {order.OrderStatus}",
|
155
|
+
caller=f"{self.__class__.__name__}.order_",
|
156
|
+
)
|
157
|
+
else:
|
158
|
+
# Insert the new order by creating an object
|
159
|
+
new_order = Order(
|
160
|
+
order.OrderUniqueIdentifier, order.AppOrderID, order.OrderStatus
|
161
|
+
)
|
162
|
+
self.order_diary.append(new_order)
|
163
|
+
if self.interactiveEngine.user_logger:
|
164
|
+
self.interactiveEngine.user_logger.info(
|
165
|
+
f"Inserted new order {order.OrderUniqueIdentifier} with status {order.OrderStatus}",
|
166
|
+
caller=f"{self.__class__.__name__}.order_",
|
167
|
+
)
|
168
|
+
asyncio.ensure_future(self.on_orderEvent(order))
|
169
|
+
|
170
|
+
@abstractmethod
|
171
|
+
async def on_orderEvent(self, message: "OrderEvent") -> None:
|
172
|
+
"""
|
173
|
+
On Order Event for strategy.
|
174
|
+
"""
|
175
|
+
...
|
176
|
+
|
177
|
+
async def trade_(self, trade: "TradeEvent") -> None:
|
178
|
+
"""
|
179
|
+
Handles a trade event by updating the position diary and triggering the on_tradeEvent callback.
|
180
|
+
Args:
|
181
|
+
trade (TradeEvent): The trade event to be processed.
|
182
|
+
Returns:
|
183
|
+
None
|
184
|
+
The method performs the following steps:
|
185
|
+
1. Checks if the trade exists in the position diary.
|
186
|
+
2. Updates the position if it exists, otherwise inserts a new position.
|
187
|
+
3. Logs the update or insertion of the position.
|
188
|
+
4. Ensures the on_tradeEvent callback is called asynchronously.
|
189
|
+
"""
|
190
|
+
|
191
|
+
# Check if the trade exists in the position diary
|
192
|
+
existing_position = next(
|
193
|
+
(
|
194
|
+
_
|
195
|
+
for _ in self.position_diary
|
196
|
+
if _.ExchangeSegment == trade.ExchangeSegment
|
197
|
+
and _.ExchangeInstrumentID == trade.ExchangeInstrumentID
|
198
|
+
and _.ProductType == trade.ProductType
|
199
|
+
),
|
200
|
+
None,
|
201
|
+
)
|
202
|
+
# Update the position
|
203
|
+
_quantity = (1 if trade.OrderSide == "Buy" else -1) * trade.OrderQuantity
|
204
|
+
if existing_position:
|
205
|
+
existing_position.Quantity += _quantity
|
206
|
+
if self.interactiveEngine.user_logger:
|
207
|
+
self.interactiveEngine.user_logger.info(
|
208
|
+
f"Updated position for trade {trade.ExchangeInstrumentID}: {existing_position.Quantity}",
|
209
|
+
caller=f"{self.__class__.__name__}.trade_",
|
210
|
+
)
|
211
|
+
else:
|
212
|
+
new_position = Position(
|
213
|
+
trade.ExchangeSegment,
|
214
|
+
trade.ExchangeInstrumentID,
|
215
|
+
trade.ProductType,
|
216
|
+
_quantity,
|
217
|
+
)
|
218
|
+
self.position_diary.append(new_position)
|
219
|
+
if self.interactiveEngine.user_logger:
|
220
|
+
self.interactiveEngine.user_logger.info(
|
221
|
+
f"Inserted new position {trade.ExchangeInstrumentID} with quantity {new_position.Quantity}",
|
222
|
+
caller=f"{self.__class__.__name__}.trade_",
|
223
|
+
)
|
224
|
+
asyncio.ensure_future(self.on_tradeEvent(trade))
|
225
|
+
|
226
|
+
@abstractmethod
|
227
|
+
def on_tradeEvent(self, message: "TradeEvent") -> None:
|
228
|
+
"""
|
229
|
+
On trade Event for strategy.
|
230
|
+
"""
|
231
|
+
...
|
232
|
+
|
233
|
+
@abstractmethod
|
234
|
+
async def unsubscribe(self) -> None:
|
235
|
+
"""
|
236
|
+
Abstract method (virtual function) to handle unsubscription logic.
|
237
|
+
Must be implemented by subclasses.
|
238
|
+
"""
|
239
|
+
...
|
240
|
+
|
241
|
+
@abstractmethod
|
242
|
+
async def initialize(self) -> None:
|
243
|
+
"""
|
244
|
+
Initialize the strategy.
|
245
|
+
This function is intended to be a one-time asynchronous setup function for the strategy.
|
246
|
+
It can be used to perform any necessary initialization tasks, such as scheduling jobs with APScheduler.
|
247
|
+
"""
|
248
|
+
# Perform initialization tasks here
|
249
|
+
...
|
250
|
+
|
251
|
+
@abstractmethod
|
252
|
+
async def deinitialize(self) -> None:
|
253
|
+
"""
|
254
|
+
Deinitialize the strategy.
|
255
|
+
This function is intended to be a one-time asynchronous teardown function for the strategy.
|
256
|
+
It can be used to perform any necessary cleanup tasks, such as unscheduling jobs with APScheduler.
|
257
|
+
"""
|
258
|
+
# Perform deinitialization tasks here
|
259
|
+
...
|
260
|
+
|
261
|
+
async def liquidateIntraday(self) -> None:
|
262
|
+
"""
|
263
|
+
Asynchronously liquidates all intraday positions and cancels pending orders.
|
264
|
+
This method iterates through the order diary and cancels any orders that are
|
265
|
+
in the "PendingNew" or "New" status. It also iterates through the position
|
266
|
+
diary and places market orders to liquidate any positions with a non-zero
|
267
|
+
quantity.
|
268
|
+
Returns:
|
269
|
+
None
|
270
|
+
"""
|
271
|
+
if self.interactiveEngine.user_logger:
|
272
|
+
self.interactiveEngine.user_logger.info(
|
273
|
+
f"Cancel open order and square off position for strategy {self.name}",
|
274
|
+
caller=f"{self.__class__.__name__}.liquidateIntraday",
|
275
|
+
)
|
276
|
+
|
277
|
+
for order in self.order_diary:
|
278
|
+
if order.OrderStatus in ["PendingNew", "New"]:
|
279
|
+
asyncio.ensure_future(
|
280
|
+
self.interactiveEngine.cancel_order(
|
281
|
+
order.AppOrderID, order.OrderUniqueIdentifier
|
282
|
+
)
|
283
|
+
)
|
284
|
+
for position in self.position_diary:
|
285
|
+
if position.Quantity != 0:
|
286
|
+
_order_no = self.order_no()
|
287
|
+
asyncio.ensure_future(
|
288
|
+
self.interactiveEngine.market_order(
|
289
|
+
position.ExchangeSegment,
|
290
|
+
position.ExchangeInstrumentID,
|
291
|
+
position.ProductType,
|
292
|
+
-1 * position.Quantity,
|
293
|
+
_order_no,
|
294
|
+
)
|
295
|
+
)
|
296
|
+
|
297
|
+
def isInvested(self) -> bool:
|
298
|
+
"""
|
299
|
+
Asynchronously checks if there are any investments by summing the quantities
|
300
|
+
of all positions in the position diary.
|
301
|
+
Returns:
|
302
|
+
bool: True if the total quantity is not zero, indicating that there are
|
303
|
+
investments. False otherwise.
|
304
|
+
"""
|
305
|
+
|
306
|
+
qty = 0
|
307
|
+
for position in self.position_diary:
|
308
|
+
qty += position.Quantity
|
309
|
+
return qty != 0
|
310
|
+
|
311
|
+
async def liquidateIntradayDummy(self) -> None:
|
312
|
+
|
313
|
+
if self.interactiveEngine.user_logger:
|
314
|
+
self.interactiveEngine.user_logger.info(
|
315
|
+
f"Cancel open order and square off position for strategy {self.name}",
|
316
|
+
caller=f"{self.__class__.__name__}.liquidateIntraday",
|
317
|
+
)
|
318
|
+
|
319
|
+
# for order in self.order_diary:
|
320
|
+
# if order.OrderStatus in ["PendingNew", "New"]:
|
321
|
+
# asyncio.ensure_future(
|
322
|
+
# self.interactiveEngine.cancel_order(
|
323
|
+
# order.AppOrderID, order.OrderUniqueIdentifier
|
324
|
+
# )
|
325
|
+
# )
|
326
|
+
for position in self.position_diary:
|
327
|
+
if position.Quantity != 0:
|
328
|
+
_order_no = self.order_no()
|
329
|
+
asyncio.ensure_future(
|
330
|
+
self.marketDataEngine.dummy_order(
|
331
|
+
position.ExchangeSegment,
|
332
|
+
position.ExchangeInstrumentID,
|
333
|
+
position.ProductType,
|
334
|
+
-1 * position.Quantity,
|
335
|
+
_order_no,
|
336
|
+
self,
|
337
|
+
)
|
338
|
+
)
|
@@ -0,0 +1,89 @@
|
|
1
|
+
from pydantic import BaseModel
|
2
|
+
from decimal import Decimal
|
3
|
+
from datetime import datetime, timezone
|
4
|
+
import json
|
5
|
+
from typing import Dict, Any
|
6
|
+
|
7
|
+
|
8
|
+
class CandleData(BaseModel):
|
9
|
+
"""
|
10
|
+
CandleData model representing candlestick data for trading.
|
11
|
+
|
12
|
+
Attributes:
|
13
|
+
MessageCode (int): The message code.
|
14
|
+
MessageVersion (int): The version of the message.
|
15
|
+
ApplicationType (int): The type of application.
|
16
|
+
TokenID (int): The token ID.
|
17
|
+
ExchangeSegment (int): The exchange segment.
|
18
|
+
BarTime (datetime): The time of the bar.
|
19
|
+
BarVolume (Decimal): The volume of the bar.
|
20
|
+
OpenInterest (Decimal): The open interest.
|
21
|
+
SumOfQtyInToPrice (Decimal): The sum of quantity into price.
|
22
|
+
ExchangeInstrumentID (int): The exchange instrument ID.
|
23
|
+
Open (Decimal): The opening price.
|
24
|
+
High (Decimal): The highest price.
|
25
|
+
Low (Decimal): The lowest price.
|
26
|
+
Close (Decimal): The closing price.
|
27
|
+
Methods:
|
28
|
+
from_string(cls, message: str):
|
29
|
+
Creates an instance of CandleData from a JSON string.
|
30
|
+
__getitem__(self, item):
|
31
|
+
Allows access to specific attributes using dictionary-like indexing.
|
32
|
+
__str__(self):
|
33
|
+
Returns a JSON string representation of the instance with selected attributes.
|
34
|
+
"""
|
35
|
+
|
36
|
+
MessageCode: int
|
37
|
+
MessageVersion: int
|
38
|
+
ApplicationType: int
|
39
|
+
TokenID: int
|
40
|
+
ExchangeSegment: int
|
41
|
+
BarTime: int
|
42
|
+
BarVolume: Decimal
|
43
|
+
OpenInterest: Decimal
|
44
|
+
SumOfQtyInToPrice: Decimal
|
45
|
+
ExchangeInstrumentID: int
|
46
|
+
BarTime: datetime
|
47
|
+
Open: Decimal
|
48
|
+
High: Decimal
|
49
|
+
Low: Decimal
|
50
|
+
Close: Decimal
|
51
|
+
|
52
|
+
def __init__(self, input_data: Any):
|
53
|
+
if isinstance(input_data, dict):
|
54
|
+
input_data["BarTime"] = datetime.fromtimestamp(
|
55
|
+
input_data["BarTime"], timezone.utc
|
56
|
+
)
|
57
|
+
super().__init__(**input_data)
|
58
|
+
elif isinstance(input_data, str):
|
59
|
+
data = json.loads(input_data)
|
60
|
+
data["BarTime"] = datetime.fromtimestamp(data["BarTime"], timezone.utc)
|
61
|
+
super().__init__(**data)
|
62
|
+
else:
|
63
|
+
raise ValueError("Unsupported input type for CandleData")
|
64
|
+
|
65
|
+
def __getitem__(self, item):
|
66
|
+
allowed_keys = {
|
67
|
+
"High",
|
68
|
+
"Low",
|
69
|
+
"Open",
|
70
|
+
"Close",
|
71
|
+
"ExchangeInstrumentID",
|
72
|
+
"BarTime",
|
73
|
+
"Volume",
|
74
|
+
}
|
75
|
+
if item in allowed_keys:
|
76
|
+
return getattr(self, item)
|
77
|
+
|
78
|
+
def __str__(self):
|
79
|
+
allowed_keys = {
|
80
|
+
"High",
|
81
|
+
"Low",
|
82
|
+
"Open",
|
83
|
+
"Close",
|
84
|
+
"ExchangeInstrumentID",
|
85
|
+
"BarTime",
|
86
|
+
"BarVolume",
|
87
|
+
}
|
88
|
+
filtered_data = {key: getattr(self, key) for key in allowed_keys}
|
89
|
+
return json.dumps(filtered_data, default=str, indent=4)
|
@@ -0,0 +1,84 @@
|
|
1
|
+
from decimal import Decimal
|
2
|
+
from pydantic import BaseModel
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from pandas.core.series import Series
|
7
|
+
|
8
|
+
|
9
|
+
class CMInstrument(BaseModel):
|
10
|
+
ExchangeSegment: str
|
11
|
+
ExchangeInstrumentID: int
|
12
|
+
InstrumentType: int
|
13
|
+
Name: str
|
14
|
+
Description: str
|
15
|
+
Series: str
|
16
|
+
NameWithSeries: str
|
17
|
+
InstrumentID: int
|
18
|
+
PriceBand_High: Decimal
|
19
|
+
PriceBand_Low: Decimal
|
20
|
+
FreezeQty: int
|
21
|
+
TickSize: Decimal
|
22
|
+
LotSize: int
|
23
|
+
Multiplier: Decimal
|
24
|
+
DisplayName: str
|
25
|
+
ISIN: str
|
26
|
+
PriceNumerator: int
|
27
|
+
PriceDenominator: int
|
28
|
+
DetailedDescription: str
|
29
|
+
ExtendedSurvlndicator: int
|
30
|
+
Cautionlndicator: int
|
31
|
+
GSMIndicator: int
|
32
|
+
|
33
|
+
def __init__(
|
34
|
+
self,
|
35
|
+
ExchangeSegment: str,
|
36
|
+
ExchangeInstrumentID: int,
|
37
|
+
InstrumentType: int,
|
38
|
+
Name: str,
|
39
|
+
Description: str,
|
40
|
+
Series: str,
|
41
|
+
NameWithSeries: str,
|
42
|
+
InstrumentID: int,
|
43
|
+
PriceBand_High: Decimal,
|
44
|
+
PriceBand_Low: Decimal,
|
45
|
+
FreezeQty: int,
|
46
|
+
TickSize: Decimal,
|
47
|
+
LotSize: int,
|
48
|
+
Multiplier: Decimal,
|
49
|
+
DisplayName: str,
|
50
|
+
ISIN: str,
|
51
|
+
PriceNumerator: int,
|
52
|
+
PriceDenominator: int,
|
53
|
+
DetailedDescription: str,
|
54
|
+
ExtendedSurvlndicator: int,
|
55
|
+
Cautionlndicator: int,
|
56
|
+
GSMIndicator: int,
|
57
|
+
):
|
58
|
+
super().__init__(
|
59
|
+
ExchangeSegment=ExchangeSegment,
|
60
|
+
ExchangeInstrumentID=ExchangeInstrumentID,
|
61
|
+
InstrumentType=InstrumentType,
|
62
|
+
Name=Name,
|
63
|
+
Description=Description,
|
64
|
+
Series=Series,
|
65
|
+
NameWithSeries=NameWithSeries,
|
66
|
+
InstrumentID=InstrumentID,
|
67
|
+
PriceBand_High=PriceBand_High,
|
68
|
+
PriceBand_Low=PriceBand_Low,
|
69
|
+
FreezeQty=FreezeQty,
|
70
|
+
TickSize=TickSize,
|
71
|
+
LotSize=LotSize,
|
72
|
+
Multiplier=Multiplier,
|
73
|
+
DisplayName=DisplayName,
|
74
|
+
ISIN=ISIN,
|
75
|
+
PriceNumerator=PriceNumerator,
|
76
|
+
PriceDenominator=PriceDenominator,
|
77
|
+
DetailedDescription=DetailedDescription,
|
78
|
+
ExtendedSurvlndicator=ExtendedSurvlndicator,
|
79
|
+
Cautionlndicator=Cautionlndicator,
|
80
|
+
GSMIndicator=GSMIndicator,
|
81
|
+
)
|
82
|
+
|
83
|
+
def __init__(self, df: "Series"):
|
84
|
+
super().__init__(**df)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
from decimal import Decimal
|
2
|
+
from pydantic import BaseModel
|
3
|
+
from datetime import datetime
|
4
|
+
from typing import TYPE_CHECKING
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from pandas.core.series import Series
|
8
|
+
|
9
|
+
class FutureInstrument(BaseModel):
|
10
|
+
ExchangeSegment: str
|
11
|
+
ExchangeInstrumentID: int
|
12
|
+
InstrumentType: int
|
13
|
+
Name: str
|
14
|
+
Description: str
|
15
|
+
Series: str
|
16
|
+
NameWithSeries: str
|
17
|
+
InstrumentID: int
|
18
|
+
PriceBand_High: Decimal
|
19
|
+
PriceBand_Low: Decimal
|
20
|
+
FreezeQty: int
|
21
|
+
TickSize: Decimal
|
22
|
+
LotSize: int
|
23
|
+
Multiplier: Decimal
|
24
|
+
ContractExpiration: datetime
|
25
|
+
DisplayName: str
|
26
|
+
PriceNumerator: int
|
27
|
+
PriceDenominator: int
|
28
|
+
DetailedDescription: str
|
29
|
+
|
30
|
+
def __init__(
|
31
|
+
self,
|
32
|
+
ExchangeSegment: str,
|
33
|
+
ExchangeInstrumentID: int,
|
34
|
+
InstrumentType: int,
|
35
|
+
Name: str,
|
36
|
+
Description: str,
|
37
|
+
Series: str,
|
38
|
+
NameWithSeries: str,
|
39
|
+
InstrumentID: int,
|
40
|
+
PriceBand_High: Decimal,
|
41
|
+
PriceBand_Low: Decimal,
|
42
|
+
FreezeQty: int,
|
43
|
+
TickSize: Decimal,
|
44
|
+
LotSize: int,
|
45
|
+
Multiplier: Decimal,
|
46
|
+
ContractExpiration: datetime,
|
47
|
+
DisplayName: str,
|
48
|
+
PriceNumerator: int,
|
49
|
+
PriceDenominator: int,
|
50
|
+
DetailedDescription: str,
|
51
|
+
):
|
52
|
+
super().__init__(
|
53
|
+
ExchangeSegment=ExchangeSegment,
|
54
|
+
ExchangeInstrumentID=ExchangeInstrumentID,
|
55
|
+
InstrumentType=InstrumentType,
|
56
|
+
Name=Name,
|
57
|
+
Description=Description,
|
58
|
+
Series=Series,
|
59
|
+
NameWithSeries=NameWithSeries,
|
60
|
+
InstrumentID=InstrumentID,
|
61
|
+
PriceBand_High=PriceBand_High,
|
62
|
+
PriceBand_Low=PriceBand_Low,
|
63
|
+
FreezeQty=FreezeQty,
|
64
|
+
TickSize=TickSize,
|
65
|
+
LotSize=LotSize,
|
66
|
+
Multiplier=Multiplier,
|
67
|
+
ContractExpiration=ContractExpiration,
|
68
|
+
DisplayName=DisplayName,
|
69
|
+
PriceNumerator=PriceNumerator,
|
70
|
+
PriceDenominator=PriceDenominator,
|
71
|
+
DetailedDescription=DetailedDescription,
|
72
|
+
)
|
73
|
+
def __init__(self, df: "Series"):
|
74
|
+
super().__init__(**df)
|