tradx 0.9__tar.gz → 0.9.2__tar.gz
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-0.9 → tradx-0.9.2}/PKG-INFO +1 -1
- tradx-0.9.2/examples/example3.py +329 -0
- {tradx-0.9 → tradx-0.9.2}/pyproject.toml +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_candleData.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_ltpData.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_ltpPartailData.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_marketDepthData.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_marketStatusData.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_openInterestData.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_openInterestPartialData.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_option.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_orderEvent.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_positionEvent.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_touchLineData.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_tradeConversionEvent.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/test/test_tradeEvent.py +1 -1
- {tradx-0.9 → tradx-0.9.2}/.gitignore +0 -0
- {tradx-0.9 → tradx-0.9.2}/.vscode/settings.json +0 -0
- {tradx-0.9 → tradx-0.9.2}/README.md +0 -0
- {tradx-0.9 → tradx-0.9.2}/examples/example1.log +0 -0
- {tradx-0.9 → tradx-0.9.2}/examples/example1.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/examples/example2.log +0 -0
- {tradx-0.9 → tradx-0.9.2}/examples/example2.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/__init__.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/algoContainer.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/baseAlgo.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/interactive/order.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/interactive/orderEvent.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/interactive/position.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/interactive/positionEvent.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/interactive/tradeConversionEvent.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/interactive/tradeEvent.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/candleData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/cmInstrument.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/futureInstrument.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/index.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/instrumentPropertyChangeData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/ltpData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/ltpPartialData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/marketDepthData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/marketStatusData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/openInterestData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/openInterestPartialData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/optionsInstrument.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/touchLineData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/baseClass/market/touchLinePartialData.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/constants/holidays.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/dualHashMap.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/interactiveEngine.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/logger/logger.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/logger/logger2.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/marketDataEngine.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/src/tradx/py.typed +0 -0
- {tradx-0.9 → tradx-0.9.2}/test/test_interactiveEngine.log +0 -0
- {tradx-0.9 → tradx-0.9.2}/test/test_interactiveEngine.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/test/test_logger.log +0 -0
- {tradx-0.9 → tradx-0.9.2}/test/test_logger.py +0 -0
- {tradx-0.9 → tradx-0.9.2}/test/test_marketDataEngine.log +0 -0
- {tradx-0.9 → tradx-0.9.2}/test/test_marketDataEngine.py +0 -0
@@ -0,0 +1,329 @@
|
|
1
|
+
# Importing modules from tradx
|
2
|
+
from tradx.marketDataEngine import marketDataEngine
|
3
|
+
from tradx.interactiveEngine import interactiveEngine
|
4
|
+
from tradx.baseClass.baseAlgo import BaseAlgo
|
5
|
+
from tradx.baseClass.market.candleData import CandleData
|
6
|
+
from tradx.baseClass.market.index import Index
|
7
|
+
from tradx.baseClass.market.optionsInstrument import OptionsInstrument, OptionType
|
8
|
+
from tradx.baseClass.interactive.orderEvent import OrderEvent
|
9
|
+
from tradx.baseClass.interactive.tradeEvent import TradeEvent
|
10
|
+
from tradx.baseClass.interactive.order import Order
|
11
|
+
from tradx.baseClass.interactive.position import Position
|
12
|
+
from tradx.logger.logger import setup_user_logger
|
13
|
+
from tradx.constants.holidays import holidays
|
14
|
+
|
15
|
+
# Importing other necessary modules
|
16
|
+
import asyncio
|
17
|
+
import os
|
18
|
+
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
19
|
+
from datetime import datetime, timedelta
|
20
|
+
from typing import List, Any
|
21
|
+
from decimal import Decimal
|
22
|
+
|
23
|
+
"""
|
24
|
+
This script demonstrates the implementation of two example algorithmic trading strategies using the `tradx` library.
|
25
|
+
It includes the following components:
|
26
|
+
Classes:
|
27
|
+
- Example1: An example trading algorithm that places a market order on current week expiry nifty call at-the-money.
|
28
|
+
Functions:
|
29
|
+
- main: The main asynchronous function that initializes the trading environment, sets up logging, and schedules the execution of the example algorithms.
|
30
|
+
Usage:
|
31
|
+
- The script loads environment variables from a .env file.
|
32
|
+
- It sets up a user logger to log trading activities.
|
33
|
+
- It initializes the market data engine and interactive engine using API keys and other configurations from the environment variables.
|
34
|
+
- It creates instances of Example1 and Example2 algorithms.
|
35
|
+
- It schedules the initialization and liquidation of the algorithms using the AsyncIOScheduler.
|
36
|
+
- It waits for user input to exit the script.
|
37
|
+
Dependencies:
|
38
|
+
- tradx.marketDataEngine
|
39
|
+
- tradx.interactiveEngine
|
40
|
+
- tradx.baseClass.baseAlgo
|
41
|
+
- tradx.baseClass.candleData
|
42
|
+
- tradx.baseClass.orderEvent
|
43
|
+
- tradx.baseClass.tradeEvent
|
44
|
+
- tradx.baseClass.order
|
45
|
+
- tradx.baseClass.position
|
46
|
+
- tradx.logger.logger
|
47
|
+
- asyncio
|
48
|
+
- os
|
49
|
+
- dotenv
|
50
|
+
- apscheduler.schedulers.asyncio
|
51
|
+
- datetime
|
52
|
+
To run the script, execute it as a standalone Python file.
|
53
|
+
"""
|
54
|
+
|
55
|
+
|
56
|
+
async def exit_input(
|
57
|
+
exit_event: asyncio.Event,
|
58
|
+
):
|
59
|
+
"""Waits for user input to exit the program."""
|
60
|
+
x = ""
|
61
|
+
event_wait_task = asyncio.ensure_future(exit_event.wait())
|
62
|
+
while x != "exit" and (not exit_event.is_set()):
|
63
|
+
try:
|
64
|
+
io_task = asyncio.ensure_future(ainput("Type 'exit' to exit\n"))
|
65
|
+
done, pending = await asyncio.wait(
|
66
|
+
[io_task, event_wait_task], return_when=asyncio.FIRST_COMPLETED
|
67
|
+
)
|
68
|
+
if event_wait_task in done:
|
69
|
+
io_task.cancel()
|
70
|
+
else:
|
71
|
+
x = done.pop().result()
|
72
|
+
if x == "exit":
|
73
|
+
exit_event.set()
|
74
|
+
except asyncio.CancelledError:
|
75
|
+
pass
|
76
|
+
print("Gracefully exiting the algorithm.")
|
77
|
+
exit_event.set()
|
78
|
+
|
79
|
+
|
80
|
+
class SIndex:
|
81
|
+
"""Class to manage index options and calculate strike prices based on given values and strike gaps."""
|
82
|
+
|
83
|
+
def __init__(
|
84
|
+
self,
|
85
|
+
symbol: Index,
|
86
|
+
strikegap: Decimal,
|
87
|
+
value: Decimal,
|
88
|
+
lot_size: int,
|
89
|
+
optionChain: List[OptionsInstrument] = [],
|
90
|
+
) -> None:
|
91
|
+
self._symbol: Index = symbol
|
92
|
+
self._strikegap: Decimal = strikegap
|
93
|
+
self._value: Decimal = value
|
94
|
+
self._lot_size: int = lot_size
|
95
|
+
self._strike: int = 0
|
96
|
+
self._prev_strike: int = None # To store the previous strike value
|
97
|
+
self._optionChain: List[OptionsInstrument] = optionChain
|
98
|
+
|
99
|
+
@property
|
100
|
+
def symbol(self) -> Index:
|
101
|
+
return self._symbol
|
102
|
+
|
103
|
+
@property
|
104
|
+
def lot_size(self) -> int:
|
105
|
+
return self._lot_size
|
106
|
+
|
107
|
+
@symbol.setter
|
108
|
+
def symbol(self, value) -> None:
|
109
|
+
if not isinstance(value, Index):
|
110
|
+
raise ValueError("Symbol must be a tradx.baseClass.Index.Index")
|
111
|
+
self._symbol = value
|
112
|
+
|
113
|
+
@property
|
114
|
+
def strikegap(self) -> Decimal:
|
115
|
+
return self._strikegap
|
116
|
+
|
117
|
+
@strikegap.setter
|
118
|
+
def strikegap(self, value) -> None:
|
119
|
+
self._strikegap = value
|
120
|
+
|
121
|
+
@property
|
122
|
+
def value(self) -> Decimal:
|
123
|
+
return self._value
|
124
|
+
|
125
|
+
@value.setter
|
126
|
+
def value(self, value: Any) -> None:
|
127
|
+
"""
|
128
|
+
Sets the value and calculates the strike price based on the given value.
|
129
|
+
Args:
|
130
|
+
value (Any): The value to be set. It will be converted to a Decimal.
|
131
|
+
Sets:
|
132
|
+
self._value (Decimal): The value converted to a Decimal.
|
133
|
+
self._strike (int): The calculated strike price based on the value and strike gap.
|
134
|
+
"""
|
135
|
+
|
136
|
+
self._value = Decimal(value)
|
137
|
+
strike = int(self._strikegap * round(self._value / self._strikegap))
|
138
|
+
self._strike = strike
|
139
|
+
|
140
|
+
@property
|
141
|
+
def strike(self) -> int:
|
142
|
+
return self._strike
|
143
|
+
|
144
|
+
@property
|
145
|
+
def previous_strike(self) -> int:
|
146
|
+
"""Returns the previous calculated strike value."""
|
147
|
+
return self._previous_strike
|
148
|
+
|
149
|
+
@property
|
150
|
+
def has_changed(self) -> bool:
|
151
|
+
"""Checks if the strike value has changed."""
|
152
|
+
return self.previous_strike != self.strike
|
153
|
+
|
154
|
+
@property
|
155
|
+
def optionChain(self) -> List[OptionsInstrument]:
|
156
|
+
return self._optionChain
|
157
|
+
|
158
|
+
@optionChain.setter
|
159
|
+
def optionChain(self, value: List[OptionsInstrument]) -> None:
|
160
|
+
if not isinstance(value, List):
|
161
|
+
raise ValueError("OptionChain must be a list")
|
162
|
+
self._optionChain = value
|
163
|
+
|
164
|
+
def search_option(
|
165
|
+
self,
|
166
|
+
StrikePrice: int = None,
|
167
|
+
OptionType: int = None,
|
168
|
+
ExchangeInstrumentID: int = None,
|
169
|
+
) -> List[OptionsInstrument]:
|
170
|
+
"""
|
171
|
+
Search for options based on specified criteria.
|
172
|
+
Args:
|
173
|
+
StrikePrice (int, optional): The strike price of the option. Must be an integer.
|
174
|
+
OptionType (int, optional): The type of the option. Must be an integer.
|
175
|
+
Returns:
|
176
|
+
List[OptionsInstrument]: A list of options that match the specified criteria.
|
177
|
+
If no criteria are specified, returns the entire option chain.
|
178
|
+
Raises:
|
179
|
+
AssertionError: If StrikePrice or OptionType are not integers.
|
180
|
+
"""
|
181
|
+
assert StrikePrice is None or isinstance(
|
182
|
+
StrikePrice, int
|
183
|
+
), "StrikePrice must be an integer"
|
184
|
+
assert OptionType is None or isinstance(
|
185
|
+
OptionType, int
|
186
|
+
), "OptionType must be an integer"
|
187
|
+
|
188
|
+
criteria = {
|
189
|
+
"StrikePrice": StrikePrice,
|
190
|
+
"OptionType": OptionType,
|
191
|
+
"ExchangeInstrumentID": ExchangeInstrumentID,
|
192
|
+
}
|
193
|
+
criteria = {k: v for k, v in criteria.items() if v is not None}
|
194
|
+
|
195
|
+
if not criteria:
|
196
|
+
return self._optionChain
|
197
|
+
|
198
|
+
results = self._optionChain
|
199
|
+
for key, value in criteria.items():
|
200
|
+
results = [option for option in results if getattr(option, key) == value]
|
201
|
+
|
202
|
+
return results
|
203
|
+
|
204
|
+
def update_previous(self) -> None:
|
205
|
+
self._previous_strike = self._strike
|
206
|
+
|
207
|
+
def end(self) -> None:
|
208
|
+
self._optionChain.clear()
|
209
|
+
|
210
|
+
|
211
|
+
class Example1(BaseAlgo):
|
212
|
+
def __init__(
|
213
|
+
self, marketDataEngine: marketDataEngine, interactiveEngine: interactiveEngine
|
214
|
+
):
|
215
|
+
super().__init__(marketDataEngine, interactiveEngine)
|
216
|
+
self.nifty = SIndex(None, 50, 0, 75)
|
217
|
+
self.marketDataEngine = marketDataEngine
|
218
|
+
self.interactiveEngine = interactiveEngine
|
219
|
+
|
220
|
+
async def initialize(self):
|
221
|
+
options = await self.marketDataEngine.option_search(
|
222
|
+
UnderlyingIndexName=self.nifty.symbol.Name, minimumExpiry=True
|
223
|
+
)
|
224
|
+
self.nifty.optionChain = options
|
225
|
+
instruments = []
|
226
|
+
instruments.append(
|
227
|
+
{
|
228
|
+
"exchangeSegment": self.nifty.symbol.ExchangeSegment,
|
229
|
+
"exchangeInstrumentID": self.nifty.symbol.ExchangeInstrumentID,
|
230
|
+
}
|
231
|
+
)
|
232
|
+
_list = await self.marketDataEngine.fetch_ltp(instruments)
|
233
|
+
nifty_index_data = next(
|
234
|
+
item
|
235
|
+
for item in _list
|
236
|
+
if item.ExchangeInstrumentID == self.nifty.symbol.ExchangeInstrumentID
|
237
|
+
)
|
238
|
+
self.nifty.value = nifty_index_data.LastTradedPrice
|
239
|
+
print(f"Nifty index value: {self.nifty.value}")
|
240
|
+
instruments.clear()
|
241
|
+
nifty_call_atm = self.nifty.search_option(
|
242
|
+
StrikePrice=self.nifty.strike,
|
243
|
+
OptionType=OptionType.CE.value,
|
244
|
+
)[0]
|
245
|
+
asyncio.ensure_future(
|
246
|
+
self.interactiveEngine.market_order(
|
247
|
+
self.interactiveEngine.xt.EXCHANGE_NSEFO,
|
248
|
+
nifty_call_atm.ExchangeInstrumentID,
|
249
|
+
"NRML",
|
250
|
+
-1 * self.nifty.lot_size,
|
251
|
+
self.order_no(),
|
252
|
+
)
|
253
|
+
)
|
254
|
+
|
255
|
+
async def deinitialize(self):
|
256
|
+
asyncio.ensure_future(self.unsubscribe())
|
257
|
+
|
258
|
+
async def on_barData(self, data: CandleData):
|
259
|
+
"""Expected Bar data here."""
|
260
|
+
self.marketDataEngine.user_logger.info(
|
261
|
+
f"Received the bar: {data}", caller=f"{self.__class__.__name__}.on_data"
|
262
|
+
)
|
263
|
+
|
264
|
+
async def on_orderEvent(self, order: OrderEvent):
|
265
|
+
self.interactiveEngine.user_logger.info(
|
266
|
+
f"Received order event: {order}",
|
267
|
+
caller=f"{self.__class__.__name__}.on_orderEvent",
|
268
|
+
)
|
269
|
+
|
270
|
+
async def on_tradeEvent(self, trade: TradeEvent):
|
271
|
+
self.interactiveEngine.user_logger.info(
|
272
|
+
f"Received trade event: {trade}",
|
273
|
+
caller=f"{self.__class__.__name__}.on_tradeEvent",
|
274
|
+
)
|
275
|
+
|
276
|
+
async def subscribe(self): ...
|
277
|
+
async def unsubscribe(self): ...
|
278
|
+
|
279
|
+
|
280
|
+
|
281
|
+
async def main():
|
282
|
+
log_file_path = os.path.join(os.path.dirname(__file__), "TEST.log")
|
283
|
+
if os.path.exists(log_file_path):
|
284
|
+
os.remove(log_file_path)
|
285
|
+
user_logger = setup_user_logger(log_file_path)
|
286
|
+
scheduler = AsyncIOScheduler()
|
287
|
+
scheduler.start()
|
288
|
+
exit_event = asyncio.Event()
|
289
|
+
tradeObject = marketDataEngine(
|
290
|
+
"MARKETDATA_API_KEY",
|
291
|
+
"MARKETDATA_API_SECRET",
|
292
|
+
"SOURCE",
|
293
|
+
"ROOT",
|
294
|
+
user_logger,
|
295
|
+
)
|
296
|
+
|
297
|
+
interactiveObj = interactiveEngine(
|
298
|
+
"INTERACTIVE_API_KEY",
|
299
|
+
"INTERACTIVE_API_SECRET",
|
300
|
+
"SOURCE",
|
301
|
+
"ROOT",
|
302
|
+
user_logger,
|
303
|
+
)
|
304
|
+
|
305
|
+
await interactiveObj.initialize()
|
306
|
+
await tradeObject.loadMaster()
|
307
|
+
algo1 = Example1(tradeObject, interactiveObj)
|
308
|
+
scheduler.add_job(
|
309
|
+
algo1.initialize, "date", run_date=datetime.now() + timedelta(seconds=20)
|
310
|
+
)
|
311
|
+
scheduler.add_job(
|
312
|
+
algo1.liquidateIntraday(), "date", run_date=datetime.now() + timedelta(seconds=40)
|
313
|
+
)
|
314
|
+
|
315
|
+
_input_task = asyncio.ensure_future(exit_input(exit_event))
|
316
|
+
await exit_event.wait()
|
317
|
+
await tradeObject.shutdown()
|
318
|
+
await interactiveObj.shutdown()
|
319
|
+
|
320
|
+
|
321
|
+
if __name__ == "__main__":
|
322
|
+
try:
|
323
|
+
"""Entering only if today is not a holiday"""
|
324
|
+
_today = datetime.today().date()
|
325
|
+
if _today in holidays:
|
326
|
+
os._exit(0)
|
327
|
+
asyncio.run(main())
|
328
|
+
except KeyboardInterrupt:
|
329
|
+
print("\n❌ Exiting...")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|