sysstra 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.
- src/__init__.py +6 -0
- src/custom_indicators.py +903 -0
- src/data/__init__.py +2 -0
- src/data/historical.py +85 -0
- src/data/live.py +77 -0
- src/orders/__init__.py +0 -0
- src/orders/kite.py +36 -0
- src/orders/orders.py +489 -0
- src/utils.py +1465 -0
- sysstra-0.1.0.dist-info/LICENSE +21 -0
- sysstra-0.1.0.dist-info/METADATA +37 -0
- sysstra-0.1.0.dist-info/RECORD +14 -0
- sysstra-0.1.0.dist-info/WHEEL +5 -0
- sysstra-0.1.0.dist-info/top_level.txt +1 -0
src/utils.py
ADDED
|
@@ -0,0 +1,1465 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import math
|
|
4
|
+
import itertools
|
|
5
|
+
import traceback as tb
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas_ta as ta
|
|
8
|
+
import requests
|
|
9
|
+
import pandas as pd
|
|
10
|
+
# from custom_indicators import *
|
|
11
|
+
# from config import *
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def change_granularity(data_df, granularity):
|
|
15
|
+
""" Function to Change Granularity for Provided Dataframe """
|
|
16
|
+
try:
|
|
17
|
+
data_df['timestamp'] = pd.to_datetime(data_df['timestamp'])
|
|
18
|
+
if 'date' in data_df.columns:
|
|
19
|
+
data_df['date'] = pd.to_datetime(data_df['date'])
|
|
20
|
+
|
|
21
|
+
if 'symbol' in data_df.columns:
|
|
22
|
+
symbols = sorted(set(data_df['symbol']))
|
|
23
|
+
else:
|
|
24
|
+
symbols = [""]
|
|
25
|
+
|
|
26
|
+
final_data_li = list()
|
|
27
|
+
|
|
28
|
+
def create_row_dict(gran_calc_list):
|
|
29
|
+
open_ = gran_calc_list[0]['open']
|
|
30
|
+
close_ = gran_calc_list[-1]['close']
|
|
31
|
+
high_ = max(gran_calc_list, key=lambda x: x['high'])['high']
|
|
32
|
+
low_ = min(gran_calc_list, key=lambda x: x['low'])['low']
|
|
33
|
+
timestamp_ = gran_calc_list[0]['timestamp']
|
|
34
|
+
|
|
35
|
+
row_dict = {'open': open_, 'high': high_, 'low': low_, 'close': close_,
|
|
36
|
+
'timestamp': timestamp_}
|
|
37
|
+
|
|
38
|
+
if 'oi' in gran_calc_list[-1]:
|
|
39
|
+
row_dict['oi'] = sum([i["oi"] for i in gran_calc_list])
|
|
40
|
+
if 'volume' in gran_calc_list[-1]:
|
|
41
|
+
row_dict['volume'] = sum([i["volume"] for i in gran_calc_list])
|
|
42
|
+
if 'previous_day_close' in gran_calc_list[0]:
|
|
43
|
+
row_dict['previous_day_close'] = gran_calc_list[0]['previous_day_close']
|
|
44
|
+
if 'curr_day_open' in gran_calc_list[0]:
|
|
45
|
+
row_dict['curr_day_open'] = gran_calc_list[0]['curr_day_open']
|
|
46
|
+
if 'symbol' in gran_calc_list[0]:
|
|
47
|
+
row_dict['symbol'] = gran_calc_list[0]['symbol']
|
|
48
|
+
if 'date' in gran_calc_list[0]:
|
|
49
|
+
row_dict['date'] = gran_calc_list[0]['date']
|
|
50
|
+
if 'underlying_stock' in gran_calc_list[0]:
|
|
51
|
+
row_dict['underlying_stock'] = gran_calc_list[0]['underlying_stock']
|
|
52
|
+
if 'strike' in gran_calc_list[0]:
|
|
53
|
+
row_dict['strike'] = gran_calc_list[0]['strike']
|
|
54
|
+
if 'option_type' in gran_calc_list[0]:
|
|
55
|
+
row_dict['option_type'] = gran_calc_list[0]['option_type']
|
|
56
|
+
if 'expiry' in gran_calc_list[0]:
|
|
57
|
+
row_dict['expiry'] = gran_calc_list[0]['expiry']
|
|
58
|
+
|
|
59
|
+
return row_dict
|
|
60
|
+
|
|
61
|
+
for symbol in symbols:
|
|
62
|
+
if symbol != 0:
|
|
63
|
+
sym_data_df = data_df[data_df['symbol'] == symbol]
|
|
64
|
+
else:
|
|
65
|
+
sym_data_df = data_df
|
|
66
|
+
|
|
67
|
+
# sym_data_df = sym_data_df.sort_values('timestamp', inplace=True)
|
|
68
|
+
sorted_sym_data_df = sym_data_df.sort_values('timestamp')
|
|
69
|
+
|
|
70
|
+
data_li = sorted_sym_data_df.to_dict('records')
|
|
71
|
+
|
|
72
|
+
st_time = data_li[0]['timestamp'].time()
|
|
73
|
+
|
|
74
|
+
gran_rows = list()
|
|
75
|
+
gran_calc_list = list()
|
|
76
|
+
|
|
77
|
+
for row in data_li:
|
|
78
|
+
if row['timestamp'].time() == st_time:
|
|
79
|
+
if gran_calc_list:
|
|
80
|
+
row_dict = create_row_dict(gran_calc_list)
|
|
81
|
+
|
|
82
|
+
gran_rows.append(row_dict)
|
|
83
|
+
|
|
84
|
+
gran_calc_list = list()
|
|
85
|
+
|
|
86
|
+
gran_calc_list.append(row)
|
|
87
|
+
|
|
88
|
+
if len(gran_calc_list) == granularity:
|
|
89
|
+
row_dict = create_row_dict(gran_calc_list)
|
|
90
|
+
|
|
91
|
+
gran_rows.append(row_dict)
|
|
92
|
+
gran_calc_list = list()
|
|
93
|
+
|
|
94
|
+
if gran_calc_list:
|
|
95
|
+
row_dict = create_row_dict(gran_calc_list)
|
|
96
|
+
|
|
97
|
+
gran_rows.append(row_dict)
|
|
98
|
+
|
|
99
|
+
new_df = pd.DataFrame(gran_rows)
|
|
100
|
+
new_df.dropna(subset=['close'], inplace=True)
|
|
101
|
+
final_data_li.extend(new_df.to_dict('records'))
|
|
102
|
+
|
|
103
|
+
final_df = pd.DataFrame(final_data_li)
|
|
104
|
+
|
|
105
|
+
except Exception as e:
|
|
106
|
+
final_df = data_df
|
|
107
|
+
log_message = f"Exception in changing the granularity of provided dataframe.\nReturning the same dataframe\n{e}\n"
|
|
108
|
+
print(log_message)
|
|
109
|
+
|
|
110
|
+
return final_df
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def calculate_swing(df, swing_setup=2, ignore_last_bar=False):
|
|
114
|
+
""" Function to calculate swing """
|
|
115
|
+
|
|
116
|
+
def func_upswing():
|
|
117
|
+
""" Checks up swing continuation conditions """
|
|
118
|
+
|
|
119
|
+
nonlocal data, horizontal_line, prev_candle, current_candle, swings, outside_bars, prev_idx, current_idx, p_hlines, p_swings
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
_outsidebar_hh_ = max(*data[data.index(horizontal_line): current_idx], horizontal_line,
|
|
123
|
+
key=lambda bar: bar['High'])
|
|
124
|
+
except:
|
|
125
|
+
_outsidebar_hh_ = swings[-1][0]
|
|
126
|
+
|
|
127
|
+
# If Outside bar and curr_candle makes highest of high and close below prev_candle low then convert to up swing
|
|
128
|
+
if (current_candle['High'] > prev_candle['High'] and current_candle['Low'] < prev_candle['Low']) and \
|
|
129
|
+
(current_candle['Close'] < prev_candle['Low']) and (current_candle['High'] > _outsidebar_hh_['High']):
|
|
130
|
+
p_hlines.append([horizontal_line, data[data.index(horizontal_line) + 1], "DOWN"])
|
|
131
|
+
try:
|
|
132
|
+
p_swings.append(
|
|
133
|
+
[max(*data[data.index(horizontal_line): current_idx + 1], horizontal_line,
|
|
134
|
+
key=lambda bar: bar['High']), "DOWN"])
|
|
135
|
+
except:
|
|
136
|
+
pass
|
|
137
|
+
outside_bars.append([current_candle, "DOWN"])
|
|
138
|
+
swings.append([current_candle, "DOWN"])
|
|
139
|
+
horizontal_line = max(data[prev_idx - swing_setup: current_idx + 1], key=lambda bar: bar['High'])
|
|
140
|
+
return horizontal_line, "DOWN"
|
|
141
|
+
|
|
142
|
+
# Check swing break means down swing starts
|
|
143
|
+
if current_candle['Close'] < horizontal_line['Low']:
|
|
144
|
+
p_hlines.append([horizontal_line, data[data.index(horizontal_line) + 1], "DOWN"])
|
|
145
|
+
try:
|
|
146
|
+
p_swings.append(
|
|
147
|
+
[max(*data[data.index(horizontal_line): current_idx + 1], horizontal_line,
|
|
148
|
+
key=lambda bar: bar['High']), "DOWN"])
|
|
149
|
+
except:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
swings.append([current_candle, "DOWN"])
|
|
153
|
+
horizontal_line = max(data[prev_idx - swing_setup: current_idx + 1], key=lambda bar: bar['High'])
|
|
154
|
+
return horizontal_line, "DOWN"
|
|
155
|
+
|
|
156
|
+
# Check is there any upswing shift
|
|
157
|
+
if current_candle['High'] > swings[-1][0]['High']:
|
|
158
|
+
swings.append([current_candle, "UP"])
|
|
159
|
+
horizontal_line = min(data[prev_idx - swing_setup: current_idx + 1], key=lambda bar: bar['Low'])
|
|
160
|
+
return horizontal_line, "UP"
|
|
161
|
+
|
|
162
|
+
# Return Up swing continues because no condition met, swing just rolling
|
|
163
|
+
return horizontal_line, "UP"
|
|
164
|
+
|
|
165
|
+
def func_downswing():
|
|
166
|
+
""" Checks down swing continuation conditions """
|
|
167
|
+
|
|
168
|
+
nonlocal data, horizontal_line, prev_candle, current_candle, swings, outside_bars, prev_idx, current_idx, p_hlines, p_swings
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
_outsidebar_ll_ = min(*data[data.index(horizontal_line): current_idx], horizontal_line,
|
|
172
|
+
key=lambda bar: bar['Low'])
|
|
173
|
+
except:
|
|
174
|
+
_outsidebar_ll_ = swings[-1][0]
|
|
175
|
+
|
|
176
|
+
# If Outside bar and curr_candle makes lowest of low and close above prev_candle high then convert to up swing
|
|
177
|
+
if (current_candle['High'] > prev_candle['High'] and current_candle['Low'] < prev_candle['Low']) and \
|
|
178
|
+
(current_candle['Close'] > prev_candle['High']) and (current_candle['Low'] < _outsidebar_ll_['Low']):
|
|
179
|
+
p_hlines.append([horizontal_line, data[data.index(horizontal_line) + 1], "UP"])
|
|
180
|
+
try:
|
|
181
|
+
p_swings.append(
|
|
182
|
+
[min(*data[data.index(horizontal_line): current_idx + 1], horizontal_line,
|
|
183
|
+
key=lambda bar: bar['Low']), "UP"])
|
|
184
|
+
except:
|
|
185
|
+
pass
|
|
186
|
+
outside_bars.append([current_candle, "UP"])
|
|
187
|
+
swings.append([current_candle, "UP"])
|
|
188
|
+
horizontal_line = min(data[prev_idx - swing_setup: current_idx + 1], key=lambda bar: bar['Low'])
|
|
189
|
+
return horizontal_line, "UP"
|
|
190
|
+
|
|
191
|
+
# Check swing break means up swing starts
|
|
192
|
+
if current_candle['Close'] > horizontal_line['High']:
|
|
193
|
+
p_hlines.append([horizontal_line, data[data.index(horizontal_line) + 1], "UP"])
|
|
194
|
+
try:
|
|
195
|
+
p_swings.append([min(*data[data.index(horizontal_line): current_idx + 1], horizontal_line, key=lambda bar: bar['Low']), "UP"])
|
|
196
|
+
except Exception as e:
|
|
197
|
+
print("exception here : {}".format(e))
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
swings.append([current_candle, "UP"])
|
|
201
|
+
horizontal_line = min(data[prev_idx - swing_setup: current_idx + 1], key=lambda bar: bar['Low'])
|
|
202
|
+
return horizontal_line, "UP"
|
|
203
|
+
|
|
204
|
+
# Check is there any downswing shift
|
|
205
|
+
if current_candle['Low'] < swings[-1][0]['Low']:
|
|
206
|
+
swings.append([current_candle, 'DOWN'])
|
|
207
|
+
horizontal_line = max(data[prev_idx - swing_setup: current_idx + 1], key=lambda bar: bar['High'])
|
|
208
|
+
return horizontal_line, "DOWN"
|
|
209
|
+
|
|
210
|
+
# Return Down swing continues because no condition met, swing just rolling
|
|
211
|
+
return horizontal_line, "DOWN"
|
|
212
|
+
|
|
213
|
+
def func_check_swing_chg():
|
|
214
|
+
if len(swings) >= 2:
|
|
215
|
+
if swings[-2][-1] != swings[-1][-1] and swings[-1][0]['timestamp'] == current_candle['timestamp']:
|
|
216
|
+
return True
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
# Initialization
|
|
221
|
+
swing_setup -= 2
|
|
222
|
+
prev_idx = 0
|
|
223
|
+
current_idx = 1
|
|
224
|
+
horizontal_line = ""
|
|
225
|
+
current_swing_type = ""
|
|
226
|
+
swings = []
|
|
227
|
+
p_hlines = []
|
|
228
|
+
p_swings = []
|
|
229
|
+
outside_bars = []
|
|
230
|
+
|
|
231
|
+
# Formatting data
|
|
232
|
+
df.reset_index(inplace=True)
|
|
233
|
+
df.rename({"datetime": "timestamp", "open": "Open", "high": "High", "low": "Low", "close": "Close"}, axis=1,
|
|
234
|
+
inplace=True)
|
|
235
|
+
df['swing'], df['swing_change'] = '', False
|
|
236
|
+
|
|
237
|
+
# Avoiding current day bar because its not completely build [Keep `False` while backtesting only]
|
|
238
|
+
if ignore_last_bar:
|
|
239
|
+
df = df.iloc[:-1, :]
|
|
240
|
+
|
|
241
|
+
data = df.to_dict('index')
|
|
242
|
+
data = [data[i] for i in sorted(list(data.keys()))]
|
|
243
|
+
|
|
244
|
+
# Start Swing creation
|
|
245
|
+
while current_idx < (len(data)):
|
|
246
|
+
prev_candle = data[prev_idx]
|
|
247
|
+
current_candle = data[current_idx]
|
|
248
|
+
# Wait till minimum bars built
|
|
249
|
+
if prev_idx <= swing_setup:
|
|
250
|
+
prev_idx += 1
|
|
251
|
+
current_idx += 1
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
##################################################
|
|
255
|
+
# Swing Initialization
|
|
256
|
+
if horizontal_line == "":
|
|
257
|
+
horizontal_line = prev_candle
|
|
258
|
+
upside_horizontal_line = max(data[prev_idx - swing_setup: prev_idx + 1], key=lambda x: x['High'])
|
|
259
|
+
downside_horizontal_line = min(data[prev_idx - swing_setup: prev_idx + 1], key=lambda x: x['Low'])
|
|
260
|
+
|
|
261
|
+
if current_swing_type == "":
|
|
262
|
+
if current_candle['Close'] > horizontal_line['High']:
|
|
263
|
+
swings.append([upside_horizontal_line, "UP"])
|
|
264
|
+
horizontal_line = min(data[prev_idx - swing_setup: current_idx + 1], key=lambda bar: bar['Low'])
|
|
265
|
+
current_swing_type = "UP"
|
|
266
|
+
elif current_candle['Close'] < horizontal_line['Low']:
|
|
267
|
+
swings.append([downside_horizontal_line, "DOWN"])
|
|
268
|
+
horizontal_line = max(data[prev_idx - swing_setup: current_idx + 1], key=lambda bar: bar['High'])
|
|
269
|
+
current_swing_type = "DOWN"
|
|
270
|
+
##################################################
|
|
271
|
+
|
|
272
|
+
# Continue Swing building
|
|
273
|
+
if current_swing_type == "UP":
|
|
274
|
+
# Continue loop till swing change
|
|
275
|
+
while current_swing_type != "DOWN":
|
|
276
|
+
if horizontal_line != current_candle:
|
|
277
|
+
horizontal_line, current_swing_type = func_upswing()
|
|
278
|
+
|
|
279
|
+
# Update df
|
|
280
|
+
df.loc[current_idx, 'swing'] = current_swing_type
|
|
281
|
+
df.loc[current_idx, 'swing_change'] = func_check_swing_chg()
|
|
282
|
+
|
|
283
|
+
prev_idx += 1
|
|
284
|
+
current_idx += 1
|
|
285
|
+
|
|
286
|
+
# Checking whether the last candle is current candle
|
|
287
|
+
if len(data) == current_idx:
|
|
288
|
+
break
|
|
289
|
+
else:
|
|
290
|
+
prev_candle = data[prev_idx]
|
|
291
|
+
current_candle = data[current_idx]
|
|
292
|
+
|
|
293
|
+
elif current_swing_type == "DOWN":
|
|
294
|
+
# Continue loop till swing change
|
|
295
|
+
while current_swing_type != "UP":
|
|
296
|
+
if horizontal_line != current_candle:
|
|
297
|
+
horizontal_line, current_swing_type = func_downswing()
|
|
298
|
+
|
|
299
|
+
# Update df
|
|
300
|
+
df.loc[current_idx, 'swing'] = current_swing_type
|
|
301
|
+
df.loc[current_idx, 'swing_change'] = func_check_swing_chg()
|
|
302
|
+
|
|
303
|
+
prev_idx += 1
|
|
304
|
+
current_idx += 1
|
|
305
|
+
|
|
306
|
+
if len(data) == current_idx:
|
|
307
|
+
break
|
|
308
|
+
else:
|
|
309
|
+
prev_candle = data[prev_idx]
|
|
310
|
+
current_candle = data[current_idx]
|
|
311
|
+
else:
|
|
312
|
+
prev_idx += 1
|
|
313
|
+
current_idx += 1
|
|
314
|
+
df.rename({"datetime": "timestamp", "Open": "open", "High": "high", "Low": "low", "Close": "close"}, axis=1,
|
|
315
|
+
inplace=True)
|
|
316
|
+
return df
|
|
317
|
+
except Exception as e:
|
|
318
|
+
print("Exception in Swing Calculation : {}".format(e))
|
|
319
|
+
return df
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def calculate_rolling_swing(df):
|
|
323
|
+
""" Function to calculate Rolling Swing """
|
|
324
|
+
try:
|
|
325
|
+
print("Calculating Rolling Swing")
|
|
326
|
+
swing_list = []
|
|
327
|
+
candles = df.to_dict('records')
|
|
328
|
+
last_swing = None
|
|
329
|
+
for indx, candle in enumerate(candles):
|
|
330
|
+
if indx >= 2:
|
|
331
|
+
prev_candle_1 = candles[indx - 1]
|
|
332
|
+
prev_candle_2 = candles[indx - 2]
|
|
333
|
+
|
|
334
|
+
# Conditions for Upswing
|
|
335
|
+
up_condition = candle["open"] > prev_candle_1["close"] and candle["open"] > prev_candle_2["close"]
|
|
336
|
+
|
|
337
|
+
# Conditions for DownSwing
|
|
338
|
+
down_condition = candle["open"] < prev_candle_1["close"] and candle["open"] < prev_candle_2["close"]
|
|
339
|
+
|
|
340
|
+
if up_condition:
|
|
341
|
+
current_swing = "UP"
|
|
342
|
+
if current_swing != last_swing:
|
|
343
|
+
last_swing = "UP"
|
|
344
|
+
swing_change = True
|
|
345
|
+
else:
|
|
346
|
+
swing_change = False
|
|
347
|
+
|
|
348
|
+
elif down_condition:
|
|
349
|
+
current_swing = "DOWN"
|
|
350
|
+
if current_swing != last_swing:
|
|
351
|
+
last_swing = "DOWN"
|
|
352
|
+
swing_change = True
|
|
353
|
+
else:
|
|
354
|
+
swing_change = False
|
|
355
|
+
else:
|
|
356
|
+
swing_change = False
|
|
357
|
+
current_swing = last_swing
|
|
358
|
+
|
|
359
|
+
candle["swing"] = current_swing
|
|
360
|
+
candle["swing_change"] = swing_change
|
|
361
|
+
|
|
362
|
+
swing_list.append(candle)
|
|
363
|
+
else:
|
|
364
|
+
candle["swing"] = None
|
|
365
|
+
candle["swing_change"] = False
|
|
366
|
+
swing_list.append(candle)
|
|
367
|
+
|
|
368
|
+
# Converting Swings Candles list to Dataframe
|
|
369
|
+
swing_df = pd.DataFrame(swing_list)
|
|
370
|
+
return swing_df
|
|
371
|
+
except Exception as e:
|
|
372
|
+
print("Exception in calculating rolling swing : {}".format(e))
|
|
373
|
+
pass
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def apply_indicators(dataframe, indicators_dict, logger=None):
|
|
377
|
+
""" Function to apply indicators on the dataframe provided """
|
|
378
|
+
try:
|
|
379
|
+
if isinstance(indicators_dict, tuple):
|
|
380
|
+
indicators_dict = indicators_dict[0]
|
|
381
|
+
|
|
382
|
+
if "ATR" in indicators_dict:
|
|
383
|
+
atr_values = ta.atr(dataframe['high'], dataframe['low'], dataframe['close'], length=indicators_dict["ATR"]["length"],
|
|
384
|
+
mamode=indicators_dict["ATR"]["mamode"])
|
|
385
|
+
atr_values = round(atr_values, 2).fillna(0)
|
|
386
|
+
dataframe["atr"] = atr_values.values.tolist()
|
|
387
|
+
|
|
388
|
+
if "ADX" in indicators_dict:
|
|
389
|
+
adx_values = ta.adx(high=dataframe['high'], low=dataframe['low'], close=dataframe['close'], length=indicators_dict["ADX"]["length"],
|
|
390
|
+
lensig=indicators_dict["ADX"]["lensig"])
|
|
391
|
+
dataframe["adx"] = round(adx_values.iloc[:, 0], 2).fillna(0).values
|
|
392
|
+
dataframe["adx_dmp"] = round(adx_values.iloc[:, 1], 2).fillna(0).values
|
|
393
|
+
dataframe["adx_dmn"] = round(adx_values.iloc[:, 2], 2).fillna(0).values
|
|
394
|
+
|
|
395
|
+
if "EMA" in indicators_dict:
|
|
396
|
+
ema_values = ta.ema(dataframe["close"], length=indicators_dict["EMA"]["length"])
|
|
397
|
+
ema_values = round(ema_values, 2).fillna(0)
|
|
398
|
+
dataframe["ema"] = ema_values.values.tolist()
|
|
399
|
+
|
|
400
|
+
if "EMA5" in indicators_dict:
|
|
401
|
+
input_series = dataframe["close"]
|
|
402
|
+
|
|
403
|
+
if indicators_dict["EMA5"]["source"] == "close":
|
|
404
|
+
input_series = dataframe["close"]
|
|
405
|
+
|
|
406
|
+
elif indicators_dict["EMA5"]["source"] == "open":
|
|
407
|
+
input_series = dataframe["open"]
|
|
408
|
+
|
|
409
|
+
elif indicators_dict["EMA5"]["source"] == "hlc3":
|
|
410
|
+
input_series = (dataframe['high'] + dataframe["low"] + dataframe["close"])/3
|
|
411
|
+
|
|
412
|
+
ema5_values = ta.ema(input_series, length=indicators_dict["EMA5"]["length"])
|
|
413
|
+
ema5_values = round(ema5_values, 2).fillna(0)
|
|
414
|
+
dataframe["ema5"] = ema5_values.values.tolist()
|
|
415
|
+
|
|
416
|
+
if "EMA9" in indicators_dict:
|
|
417
|
+
ema9_values = ta.ema(dataframe["close"], length=indicators_dict["EMA9"]["length"])
|
|
418
|
+
ema9_values = round(ema9_values, 2).fillna(0)
|
|
419
|
+
dataframe["ema9"] = ema9_values.values.tolist()
|
|
420
|
+
|
|
421
|
+
if "EMA12" in indicators_dict:
|
|
422
|
+
ema12_values = ta.ema(dataframe["close"], length=indicators_dict["EMA12"]["length"])
|
|
423
|
+
ema12_values = round(ema12_values, 2).fillna(0)
|
|
424
|
+
dataframe["ema12"] = ema12_values.values.tolist()
|
|
425
|
+
|
|
426
|
+
if "EMA15" in indicators_dict:
|
|
427
|
+
ema15_values = ta.ema(dataframe["close"], length=indicators_dict["EMA15"]["length"])
|
|
428
|
+
ema15_values = round(ema15_values, 2).fillna(0)
|
|
429
|
+
dataframe["ema15"] = ema15_values.values.tolist()
|
|
430
|
+
|
|
431
|
+
if "EMA20" in indicators_dict:
|
|
432
|
+
ema20_values = ta.ema(dataframe["close"], length=indicators_dict["EMA20"]["length"])
|
|
433
|
+
ema20_values = round(ema20_values, 2).fillna(0)
|
|
434
|
+
dataframe["ema20"] = ema20_values.values.tolist()
|
|
435
|
+
|
|
436
|
+
if "EMA50" in indicators_dict:
|
|
437
|
+
ema50_values = ta.ema(dataframe["close"], length=indicators_dict["EMA50"]["length"])
|
|
438
|
+
ema50_values = round(ema50_values, 2).fillna(0)
|
|
439
|
+
dataframe["ema50"] = ema50_values.values.tolist()
|
|
440
|
+
|
|
441
|
+
if "EMA60" in indicators_dict:
|
|
442
|
+
ema60_values = ta.ema(dataframe["close"], length=indicators_dict["EMA60"]["length"])
|
|
443
|
+
ema60_values = round(ema60_values, 2).fillna(0)
|
|
444
|
+
dataframe["ema60"] = ema60_values.values.tolist()
|
|
445
|
+
|
|
446
|
+
if "SMA" in indicators_dict:
|
|
447
|
+
sma_values = ta.sma(dataframe["close"], length=indicators_dict["SMA"]["length"])
|
|
448
|
+
sma_values = round(sma_values, 2).fillna(0)
|
|
449
|
+
dataframe["sma"] = sma_values.values.tolist()
|
|
450
|
+
|
|
451
|
+
if "SMA5" in indicators_dict:
|
|
452
|
+
sma_values = ta.sma(dataframe["close"], length=indicators_dict["SMA5"]["length"])
|
|
453
|
+
sma_values = round(sma_values, 2).fillna(0)
|
|
454
|
+
dataframe["sma5"] = sma_values.values.tolist()
|
|
455
|
+
|
|
456
|
+
if "SMA9" in indicators_dict:
|
|
457
|
+
sma_values = ta.sma(dataframe["close"], length=indicators_dict["SMA9"]["length"])
|
|
458
|
+
sma_values = round(sma_values, 2).fillna(0)
|
|
459
|
+
dataframe["sma9"] = sma_values.values.tolist()
|
|
460
|
+
|
|
461
|
+
if "SMA15" in indicators_dict:
|
|
462
|
+
sma_values = ta.sma(dataframe["close"], length=indicators_dict["SMA15"]["length"])
|
|
463
|
+
sma_values = round(sma_values, 2).fillna(0)
|
|
464
|
+
dataframe["sma15"] = sma_values.values.tolist()
|
|
465
|
+
|
|
466
|
+
if "SMA20" in indicators_dict:
|
|
467
|
+
sma_values = ta.sma(dataframe["close"], length=indicators_dict["SMA20"]["length"])
|
|
468
|
+
sma_values = round(sma_values, 2).fillna(0)
|
|
469
|
+
dataframe["sma20"] = sma_values.values.tolist()
|
|
470
|
+
|
|
471
|
+
if "SMA200" in indicators_dict:
|
|
472
|
+
sma_values = ta.sma(dataframe["close"], length=indicators_dict["SMA200"]["length"])
|
|
473
|
+
if sma_values is not None:
|
|
474
|
+
sma_values = round(sma_values, 2).fillna(0)
|
|
475
|
+
dataframe["sma200"] = sma_values.values.tolist()
|
|
476
|
+
else:
|
|
477
|
+
dataframe["sma200"] = 0
|
|
478
|
+
|
|
479
|
+
if "ECR" in indicators_dict:
|
|
480
|
+
ecr_values = dataframe["ema"] - dataframe["ema70"]
|
|
481
|
+
ecr_values = round(ecr_values, 2).fillna(0)
|
|
482
|
+
dataframe["ecr"] = ecr_values.tolist()
|
|
483
|
+
|
|
484
|
+
if "STOCH" in indicators_dict:
|
|
485
|
+
stoch_df = stochastic_oscillator(dataframe=dataframe, k=indicators_dict["STOCH"]["k"], d=indicators_dict["STOCH"]["d"],
|
|
486
|
+
smooth_k=indicators_dict["STOCH"]["smooth"])
|
|
487
|
+
# stoch_df = TA.stoch(high=dataframe["high"], low=dataframe["low"], close=dataframe["close"],
|
|
488
|
+
# k=indicators_dict["STOCH"]["k"], d=indicators_dict["STOCH"]["d"], smooth_k=indicators_dict["STOCH"]["smooth"], offset=0)
|
|
489
|
+
stoch_k = round(stoch_df.iloc[:, 0], 2).fillna(0).values
|
|
490
|
+
stoch_d = round(stoch_df.iloc[:, 1], 2).fillna(0).values
|
|
491
|
+
dataframe["stoch_k"] = stoch_k
|
|
492
|
+
dataframe["stoch_d"] = stoch_d
|
|
493
|
+
|
|
494
|
+
if "RSI" in indicators_dict:
|
|
495
|
+
rsi_df = ta.rsi(close=dataframe.close, length=indicators_dict["RSI"]["length"])
|
|
496
|
+
dataframe["rsi"] = round(rsi_df.iloc[:], 2).fillna(0).values
|
|
497
|
+
|
|
498
|
+
if "TVRSI" in indicators_dict:
|
|
499
|
+
tv_rsi_df = tv_rsi(dataframe=dataframe, rsi_length=indicators_dict["TVRSI"]["rsi_length"], source=indicators_dict["TVRSI"]["source"],
|
|
500
|
+
ma_length=indicators_dict["TVRSI"]["ma_length"], ma_type=indicators_dict["TVRSI"]["ma_type"],
|
|
501
|
+
bb_mult=indicators_dict["TVRSI"]["bb_mult"])
|
|
502
|
+
dataframe["tv_rsi"] = round(tv_rsi_df['rsi'], 2).fillna(0).values
|
|
503
|
+
dataframe["tv_rsi_ma"] = round(tv_rsi_df['rsi_ma'], 2).fillna(0).values
|
|
504
|
+
|
|
505
|
+
if "STOCHRSI" in indicators_dict:
|
|
506
|
+
out = ta.stochrsi(dataframe[indicators_dict["STOCHRSI"]["rsi_source"]], length=indicators_dict["STOCHRSI"]["stochastic_length"], k=indicators_dict["STOCHRSI"]["k"], d=indicators_dict["STOCHRSI"]["d"],
|
|
507
|
+
rsi_length=indicators_dict["STOCHRSI"]["rsi_length"])
|
|
508
|
+
dataframe["stochrsi_k"] = round(out.iloc[:, 0], 2).fillna(0).values
|
|
509
|
+
dataframe["stochrsi_d"] = round(out.iloc[:, 1], 2).fillna(0).values
|
|
510
|
+
|
|
511
|
+
if "MACD" in indicators_dict:
|
|
512
|
+
macd_df = ta.macd(close=dataframe[indicators_dict["MACD"]["source"]], fast=indicators_dict["MACD"]["fast_length"],
|
|
513
|
+
slow=indicators_dict["MACD"]["slow_length"], signal=indicators_dict["MACD"]["signal_smoothing"])
|
|
514
|
+
dataframe["macd"] = round(macd_df.iloc[:, 0], 2).fillna(0).values
|
|
515
|
+
dataframe["macd_h"] = round(macd_df.iloc[:, 1], 2).fillna(0).values
|
|
516
|
+
dataframe["macd_s"] = round(macd_df.iloc[:, 2], 2).fillna(0).values
|
|
517
|
+
|
|
518
|
+
if "BBAND" in indicators_dict:
|
|
519
|
+
bband_df = ta.bbands(close=dataframe[indicators_dict["BBAND"]["source"]], length=indicators_dict["BBAND"]["length"], std=indicators_dict["BBAND"]["std_dev"],
|
|
520
|
+
mamode=indicators_dict["BBAND"]["basis_ma_type"], offset=indicators_dict["BBAND"]["offset"])
|
|
521
|
+
dataframe["bband_l"] = round(bband_df.iloc[:, 0], 2).fillna(0).values
|
|
522
|
+
dataframe["bband_m"] = round(bband_df.iloc[:, 1], 2).fillna(0).values
|
|
523
|
+
dataframe["bband_u"] = round(bband_df.iloc[:, 2], 2).fillna(0).values
|
|
524
|
+
dataframe["bband_b"] = round(bband_df.iloc[:, 3], 2).fillna(0).values
|
|
525
|
+
dataframe["bband_p"] = round(bband_df.iloc[:, 4], 2).fillna(0).values
|
|
526
|
+
|
|
527
|
+
if "FISHER" in indicators_dict:
|
|
528
|
+
fisher_df = ta.fisher(high=dataframe["high"], low=dataframe["low"], length=indicators_dict["FISHER"]["length"], signal=indicators_dict["FISHER"]["signal"])
|
|
529
|
+
dataframe["fisher_t"] = round(fisher_df.iloc[:, 0], 2).fillna(0).values
|
|
530
|
+
dataframe["fisher_s"] = round(fisher_df.iloc[:, 1], 2).fillna(0).values
|
|
531
|
+
|
|
532
|
+
if "MOM" in indicators_dict:
|
|
533
|
+
mom_df = ta.mom(close=dataframe[indicators_dict["MOM"]["source"]], length=indicators_dict["MOM"]["length"])
|
|
534
|
+
dataframe["mom"] = round(mom_df.iloc[:], 2).fillna(0).values
|
|
535
|
+
|
|
536
|
+
if "ROC" in indicators_dict:
|
|
537
|
+
roc_df = ta.roc(close=dataframe[indicators_dict["ROC"]["source"]], length=indicators_dict["ROC"]["length"])
|
|
538
|
+
dataframe["roc"] = round(roc_df.iloc[:], 2).fillna(0).values
|
|
539
|
+
|
|
540
|
+
if "DPO" in indicators_dict:
|
|
541
|
+
dpo_df = ta.dpo(close=dataframe["close"], length=indicators_dict["DPO"]["length"])
|
|
542
|
+
dataframe["dpo"] = round(dpo_df.iloc[:], 2).fillna(0).values
|
|
543
|
+
|
|
544
|
+
if "RVGI" in indicators_dict:
|
|
545
|
+
rvgi_df = ta.rvgi(open_=dataframe["open"], high=dataframe["high"], low=dataframe["low"], close=dataframe["close"], length=indicators_dict["RVGI"]["length"],)
|
|
546
|
+
dataframe["rvgi"] = round(rvgi_df.iloc[:, 0], 2).fillna(0).values
|
|
547
|
+
dataframe["rvgi_s"] = round(rvgi_df.iloc[:, 1], 2).fillna(0).values
|
|
548
|
+
|
|
549
|
+
if "WILLR" in indicators_dict:
|
|
550
|
+
willr_df = ta.willr(high=dataframe["high"], low=dataframe["low"], close=dataframe["close"], length=indicators_dict["WILLR"]["length"])
|
|
551
|
+
dataframe["willr"] = round(willr_df.iloc[:], 2).fillna(0).values
|
|
552
|
+
|
|
553
|
+
if "VFI" in indicators_dict:
|
|
554
|
+
vfi_df = volume_flow_indicator(dataframe=dataframe, length=indicators_dict["VFI"]["length"], coef=indicators_dict["VFI"]["coef"],
|
|
555
|
+
vcoef=indicators_dict["VFI"]["vcoef"], signal_length=indicators_dict["VFI"]["signal_length"],
|
|
556
|
+
smooth_vfi=indicators_dict["VFI"]["smooth_vfi"])
|
|
557
|
+
dataframe["vfi"] = round(vfi_df['vfi'], 2).fillna(0).values
|
|
558
|
+
dataframe["vfi_ma"] = round(vfi_df['vfi_ma'], 2).fillna(0).values
|
|
559
|
+
dataframe["vfi_d"] = round(vfi_df['vfi_d'], 2).fillna(0).values
|
|
560
|
+
|
|
561
|
+
if "VORTEX" in indicators_dict:
|
|
562
|
+
vortex_df = ta.vortex(high=dataframe["high"], low=dataframe["low"], close=dataframe["close"], length=indicators_dict["VORTEX"]["length"])
|
|
563
|
+
dataframe["vtx_p"] = round(vortex_df.iloc[:, 0], 2).fillna(0).values
|
|
564
|
+
dataframe["vtx_m"] = round(vortex_df.iloc[:, 1], 2).fillna(0).values
|
|
565
|
+
|
|
566
|
+
if "SUPERTREND" in indicators_dict:
|
|
567
|
+
supertrend_df = ta.supertrend(high=dataframe["high"], low=dataframe["low"], close=dataframe["close"], length=indicators_dict["SUPERTREND"]["length"], multiplier=indicators_dict["SUPERTREND"]["multiplier"])
|
|
568
|
+
dataframe["supertrend"] = round(supertrend_df.iloc[:, 0], 2).fillna(0).values
|
|
569
|
+
dataframe["supertrend_d"] = round(supertrend_df.iloc[:, 1], 2).fillna(0).values
|
|
570
|
+
|
|
571
|
+
if "FRACTAL" in indicators_dict:
|
|
572
|
+
dataframe = williams_fractal(dataframe, fractal_window=indicators_dict["FRACTAL"]["length"])
|
|
573
|
+
dataframe["fractal_up"] = dataframe["fractal_up"].fillna(0).values
|
|
574
|
+
dataframe["fractal_down"] = dataframe["fractal_down"].fillna(0).values
|
|
575
|
+
|
|
576
|
+
if "TSI" in indicators_dict:
|
|
577
|
+
tsi_df = ta.tsi(close=dataframe[indicators_dict["TSI"]["source"]], fast=indicators_dict["TSI"]["fast"], slow=indicators_dict["TSI"]["slow"], signal=indicators_dict["TSI"]["signal"])
|
|
578
|
+
dataframe["tsi"] = round(tsi_df.iloc[:, 0]/100, 4).fillna(0).values
|
|
579
|
+
dataframe["tsi_s"] = round(tsi_df.iloc[:, 1]/100, 4).fillna(0).values
|
|
580
|
+
|
|
581
|
+
if "SMI" in indicators_dict:
|
|
582
|
+
smi_df = ta.smi(close=dataframe[indicators_dict["SMI"]["source"]], fast=indicators_dict["SMI"]["fast"], slow=indicators_dict["SMI"]["slow"], signal=indicators_dict["SMI"]["signal"])
|
|
583
|
+
dataframe["smi"] = round(smi_df.iloc[:, 0], 2).fillna(0).values
|
|
584
|
+
dataframe["smi_s"] = round(smi_df.iloc[:, 1], 2).fillna(0).values
|
|
585
|
+
dataframe["smi_o"] = round(smi_df.iloc[:, 2], 2).fillna(0).values
|
|
586
|
+
|
|
587
|
+
if "MCGD" in indicators_dict:
|
|
588
|
+
mcgd_df = mcgd(close=dataframe["close"], length=indicators_dict["MCGD"]["length"])
|
|
589
|
+
dataframe["mcgd"] = round(mcgd_df.iloc[:], 2).fillna(0).values
|
|
590
|
+
|
|
591
|
+
if "TPR" in indicators_dict:
|
|
592
|
+
dataframe["tpr"] = round((dataframe["high"] + dataframe["low"] + dataframe["close"])/3, 2)
|
|
593
|
+
|
|
594
|
+
if "MAC" in indicators_dict:
|
|
595
|
+
mac_u = ta.sma(close=dataframe["high"], length=indicators_dict["MAC"]["upper_length"])
|
|
596
|
+
mac_l = ta.sma(close=dataframe["low"], length=indicators_dict["MAC"]["lower_length"])
|
|
597
|
+
dataframe["mac_u"] = round(mac_u, 2).fillna(0).values
|
|
598
|
+
dataframe["mac_l"] = round(mac_l, 2).fillna(0).values
|
|
599
|
+
|
|
600
|
+
if "SQZMOM" in indicators_dict:
|
|
601
|
+
sqzmom_df = squeeze_momentum(dataframe=dataframe, bb_length=indicators_dict["SQZMOM"]["bb_length"], bb_mult=indicators_dict["SQZMOM"]["bb_mult"],
|
|
602
|
+
kc_length=indicators_dict["SQZMOM"]["kc_length"], kc_mult=indicators_dict["SQZMOM"]["kc_mult"],use_true_range=indicators_dict["SQZMOM"]["use_true_range"])
|
|
603
|
+
dataframe["sqzmom"] = round(sqzmom_df, 2).fillna(0).values
|
|
604
|
+
|
|
605
|
+
if "CHAIKIN" in indicators_dict:
|
|
606
|
+
cv_df = chaikin_volatility(dataframe=dataframe, length=indicators_dict["CHAIKIN"]["length"], roc_length=indicators_dict["CHAIKIN"]["roc_length"])
|
|
607
|
+
dataframe["chaikin"] = round(cv_df.iloc[:], 2).fillna(0).values
|
|
608
|
+
|
|
609
|
+
if "VWAP" in indicators_dict:
|
|
610
|
+
# vwap_df = TA.vwap(high=dataframe["high"], low=dataframe["low"], close=dataframe["close"], volume=dataframe["volume"])
|
|
611
|
+
vwap_df = calculate_vwap(dataframe=dataframe)
|
|
612
|
+
dataframe["vwap"] = round(vwap_df.iloc[:], 2).fillna(0).values
|
|
613
|
+
|
|
614
|
+
if "DMI" in indicators_dict:
|
|
615
|
+
dmi_df = calculate_dmi(dataframe=dataframe, adx_smoothing=indicators_dict["DMI"]["adx_smoothing"], di_length=indicators_dict["DMI"]["di_length"])
|
|
616
|
+
dataframe["di_plus"] = round(dmi_df['di_plus'], 2).fillna(0).values
|
|
617
|
+
dataframe["di_minus"] = round(dmi_df['di_minus'], 2).fillna(0).values
|
|
618
|
+
dataframe["di_adx"] = round(dmi_df['di_adx'], 2).fillna(0).values
|
|
619
|
+
|
|
620
|
+
if "YONO" in indicators_dict:
|
|
621
|
+
# dc_df = direction_change(dataframe=dataframe, depth=indicators_dict["DC"]["depth"], deviation=indicators_dict["DC"]["deviation"], backstep=indicators_dict["DC"]["backstep"])
|
|
622
|
+
yono_df = yono(dataframe=dataframe, depth=indicators_dict["YONO"]["depth"], deviation=indicators_dict["YONO"]["deviation"], backstep=indicators_dict["YONO"]["backstep"])
|
|
623
|
+
# yono_df = calculate_direction(dataframe=dataframe, depth=indicators_dict["YONO"]["depth"], deviation=indicators_dict["YONO"]["deviation"], backstep=indicators_dict["YONO"]["backstep"])
|
|
624
|
+
dataframe["yono"] = round(yono_df.iloc[:], 2).fillna(0).values
|
|
625
|
+
|
|
626
|
+
if "DEMA" in indicators_dict:
|
|
627
|
+
dema_df = ta.dema(close=dataframe[indicators_dict["DEMA"]["source"]], length=indicators_dict["DEMA"]["length"])
|
|
628
|
+
dataframe["dema"] = round(dema_df.iloc[:], 2).fillna(0).values
|
|
629
|
+
|
|
630
|
+
if "TEMA" in indicators_dict:
|
|
631
|
+
tema_df = ta.tema(close=dataframe[indicators_dict["TEMA"]["source"]], length=indicators_dict["TEMA"]["length"])
|
|
632
|
+
dataframe["tema"] = round(tema_df.iloc[:], 2).fillna(0).values
|
|
633
|
+
|
|
634
|
+
if "RVI" in indicators_dict:
|
|
635
|
+
rvi_df = ta.rvi(close=dataframe["close"], high=dataframe["high"], low=dataframe["low"], length=indicators_dict["RVI"]["length"])
|
|
636
|
+
dataframe["rvi"] = round(rvi_df.iloc[:], 2).fillna(0).values
|
|
637
|
+
|
|
638
|
+
if "SHA" in indicators_dict:
|
|
639
|
+
sha_df = heikin_ashi_smoothed(dataframe=dataframe, ema_length=indicators_dict["SHA"]["length"])
|
|
640
|
+
dataframe["sha_1"] = round(sha_df.iloc[:, 0], 2).fillna(0).values
|
|
641
|
+
dataframe["sha_2"] = round(sha_df.iloc[:, 1], 2).fillna(0).values
|
|
642
|
+
|
|
643
|
+
if "JMA" in indicators_dict:
|
|
644
|
+
jma_df = jurik_moving_average(dataframe=dataframe, length=indicators_dict["JMA"]["length"], phase=indicators_dict["JMA"]["phase"],
|
|
645
|
+
power=indicators_dict["JMA"]["power"], source=indicators_dict["JMA"]["source"])
|
|
646
|
+
dataframe["jma"] = round(jma_df.iloc[:, 0], 2).fillna(0).values
|
|
647
|
+
dataframe["jma_c"] = jma_df.iloc[:, 1].fillna(0).values
|
|
648
|
+
|
|
649
|
+
if "EFI" in indicators_dict:
|
|
650
|
+
efi_df = ta.efi(close=dataframe["close"], volume=dataframe["volume"], length=indicators_dict["EFI"]["length"])
|
|
651
|
+
dataframe["efi"] = round(efi_df, 2).fillna(0).values
|
|
652
|
+
|
|
653
|
+
if "PSAR" in indicators_dict:
|
|
654
|
+
psar_df = ta.psar(high=dataframe["high"], low=dataframe["low"], close=dataframe["close"], af0=indicators_dict["PSAR"]["start"],
|
|
655
|
+
af=indicators_dict["PSAR"]["increment"], max_af=indicators_dict["PSAR"]["max_value"])
|
|
656
|
+
dataframe["psar_l"] = round(psar_df.iloc[:, 0], 2).fillna(0).values
|
|
657
|
+
dataframe["psar_s"] = round(psar_df.iloc[:, 1], 2).fillna(0).values
|
|
658
|
+
dataframe["psar_af"] = round(psar_df.iloc[:, 2], 2).fillna(0).values
|
|
659
|
+
dataframe["psar_r"] = round(psar_df.iloc[:, 3], 2).fillna(0).values
|
|
660
|
+
|
|
661
|
+
if "AVGVOL" in indicators_dict:
|
|
662
|
+
avg_vol = average_volume(dataframe["volume"], length=indicators_dict["AVGVOL"]["length"])
|
|
663
|
+
dataframe["avg_vol"] = round(avg_vol, 2).fillna(0).values
|
|
664
|
+
|
|
665
|
+
if "BBPS" in indicators_dict:
|
|
666
|
+
|
|
667
|
+
bbps_df = bb_sideways(dataframe=dataframe, bb_length=indicators_dict["BBPS"]["bb_length"], bb_mult=indicators_dict["BBPS"]["bb_mult"],
|
|
668
|
+
bbr_len=indicators_dict["BBPS"]["bbr_len"], bbr_std_thresh=indicators_dict["BBPS"]["bbr_std_thresh"])
|
|
669
|
+
dataframe["bbps_sideways"] = bbps_df.iloc[:, 0].fillna(False).values
|
|
670
|
+
dataframe["bbps_color"] = bbps_df.iloc[:, 1].fillna(False).values
|
|
671
|
+
|
|
672
|
+
if "RDX" in indicators_dict:
|
|
673
|
+
rdx_df = calculate_rdx(dataframe=dataframe)
|
|
674
|
+
dataframe["rdx"] = round(rdx_df.iloc[:, 0]).values
|
|
675
|
+
|
|
676
|
+
if "MTI" in indicators_dict:
|
|
677
|
+
mti_df = calculate_mti(dataframe=dataframe, bb_length=indicators_dict["MTI"]["bb_length"], bb_mult=indicators_dict["MTI"]["bb_mult"],
|
|
678
|
+
adx_length=indicators_dict["MTI"]["adx_length"], rsi_length=indicators_dict["MTI"]["rsi_length"])
|
|
679
|
+
|
|
680
|
+
if "SADX" in indicators_dict:
|
|
681
|
+
s_adx = smoothed_adx(dataframe=dataframe, adx_length=indicators_dict["SADX"]["adx_length"], di_length=indicators_dict["SADX"]["di_length"],
|
|
682
|
+
smoothing_length=indicators_dict["SADX"]["smoothing_length"], mamode=indicators_dict["SADX"]["mamode"])
|
|
683
|
+
dataframe["adx"] = round(s_adx.iloc[:, 0], 2).fillna(0).values
|
|
684
|
+
dataframe["s_adx"] = round(s_adx.iloc[:, 1], 2).fillna(0).values
|
|
685
|
+
|
|
686
|
+
if "NETVOLUME" in indicators_dict:
|
|
687
|
+
net_volume = calculate_net_volume(dataframe=dataframe)
|
|
688
|
+
dataframe["net_volume"] = round(net_volume, 2).fillna(0).values
|
|
689
|
+
|
|
690
|
+
if "PPO" in indicators_dict:
|
|
691
|
+
ppo_df = ta.ppo(close=dataframe["close"], fast=indicators_dict["PPO"]["fast_length"],
|
|
692
|
+
slow=indicators_dict["PPO"]["slow_length"])
|
|
693
|
+
dataframe["ppo"] = round(ppo_df.iloc[:, 0], 2).fillna(0).values
|
|
694
|
+
dataframe["ppo_h"] = round(ppo_df.iloc[:, 1], 2).fillna(0).values
|
|
695
|
+
dataframe["ppo_s"] = round(ppo_df.iloc[:, 2], 2).fillna(0).values
|
|
696
|
+
|
|
697
|
+
if "VOSC" in indicators_dict:
|
|
698
|
+
v_osc = volume_oscillator(dataframe=dataframe, short_length=indicators_dict["VOSC"]["short_length"],
|
|
699
|
+
long_length=indicators_dict["VOSC"]["long_length"])
|
|
700
|
+
dataframe["v_osc"] = round(v_osc, 2).fillna(0).values
|
|
701
|
+
|
|
702
|
+
if "VOLUMESLOPE" in indicators_dict:
|
|
703
|
+
vol_slope_df = volume_slope(volume=dataframe["volume"], length=indicators_dict["VOLUMESLOPE"]["length"])
|
|
704
|
+
dataframe["vol_ma"] = round(vol_slope_df.iloc[:, 0], 2).fillna(0).values
|
|
705
|
+
dataframe["vol_slope"] = vol_slope_df.iloc[:, 1]
|
|
706
|
+
|
|
707
|
+
if "OISLOPE" in indicators_dict:
|
|
708
|
+
oi_slope_df = oi_slope(oi=dataframe["oi"], length=indicators_dict["OISLOPE"]["length"])
|
|
709
|
+
dataframe["oi_ma"] = round(oi_slope_df.iloc[:, 0], 2).fillna(0).values
|
|
710
|
+
dataframe["oi_slope"] = oi_slope_df.iloc[:, 1]
|
|
711
|
+
|
|
712
|
+
if "BBW" in indicators_dict:
|
|
713
|
+
bbw_df = bollinger_bandwidth(dataframe=dataframe, length=indicators_dict["BBW"]["length"], std_dev=indicators_dict["BBW"]["std_dev"],
|
|
714
|
+
source=indicators_dict["BBW"]["source"], he_length=indicators_dict["BBW"]["he_length"],
|
|
715
|
+
lc_length=indicators_dict["BBW"]["lc_length"])
|
|
716
|
+
dataframe["bbw"] = round(bbw_df, 2).fillna(0).values
|
|
717
|
+
|
|
718
|
+
if "BBWRANGE" in indicators_dict:
|
|
719
|
+
bbw_range = calculate_bbw_range(dataframe=dataframe, length=indicators_dict["BBWRANGE"]["length"], deviation=indicators_dict["BBWRANGE"]["deviation"])
|
|
720
|
+
dataframe["bbw_range"] = bbw_range
|
|
721
|
+
|
|
722
|
+
if "TSI" in indicators_dict:
|
|
723
|
+
tsi_df = calculate_tsi(close=dataframe["close"], period=indicators_dict["TSI"]["period"])
|
|
724
|
+
dataframe["tsi"] = round(tsi_df, 2).fillna(0).values
|
|
725
|
+
|
|
726
|
+
return dataframe
|
|
727
|
+
|
|
728
|
+
except Exception as e:
|
|
729
|
+
if logger:
|
|
730
|
+
logger.exception(f"Exception in adding indicators : {e}")
|
|
731
|
+
print("Exception in adding indicators : {}".format(e))
|
|
732
|
+
pass
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
def create_mt_report(trade_report, initial_deposit, file_name, logs_coll):
|
|
736
|
+
"""Function to Create Report in MetaTrader Report Format"""
|
|
737
|
+
try:
|
|
738
|
+
if not trade_report:
|
|
739
|
+
return {}
|
|
740
|
+
|
|
741
|
+
report_df = pd.DataFrame(trade_report)
|
|
742
|
+
|
|
743
|
+
mt_dict = dict()
|
|
744
|
+
mt_dict['initial_deposit'] = initial_deposit
|
|
745
|
+
|
|
746
|
+
profit_trades = list(report_df[report_df['pnl'] > 0]['pnl'])
|
|
747
|
+
loss_trades = list(report_df[report_df['pnl'] < 0]['pnl'])
|
|
748
|
+
|
|
749
|
+
# Calculating Gross Profit
|
|
750
|
+
gross_profit = round(sum(profit_trades), 2) if profit_trades else 0
|
|
751
|
+
mt_dict['gross_profit'] = gross_profit
|
|
752
|
+
|
|
753
|
+
# Calculating Gross Loss
|
|
754
|
+
gross_loss = round(sum(loss_trades), 2) if loss_trades else 0
|
|
755
|
+
mt_dict['gross_loss'] = gross_loss
|
|
756
|
+
|
|
757
|
+
# Calculating Profit Factor
|
|
758
|
+
mt_dict['profit_factor'] = round(abs(gross_profit / gross_loss), 2) if gross_loss else 0
|
|
759
|
+
|
|
760
|
+
profitside = (len(profit_trades) / len(report_df)) * (gross_profit / len(profit_trades)) if profit_trades else 0
|
|
761
|
+
lossside = (len(loss_trades) / len(report_df)) * (gross_loss / len(loss_trades)) if loss_trades else 0
|
|
762
|
+
|
|
763
|
+
expected_payoff = round(profitside - lossside, 2)
|
|
764
|
+
mt_dict['expected_payoff'] = expected_payoff
|
|
765
|
+
|
|
766
|
+
equity_history = [initial_deposit]
|
|
767
|
+
|
|
768
|
+
# report_pnl = [initial_deposit]
|
|
769
|
+
# dates = [list(report_df['date'])[0]] + sorted(set(report_df['date']))
|
|
770
|
+
|
|
771
|
+
report_df['date'] = pd.to_datetime(report_df['date'], format="%Y-%m-%d %H:%M:%S")
|
|
772
|
+
|
|
773
|
+
eq_dates = [list(report_df['date'])[0].strftime('%Y-%m-%d')] + [i.strftime('%Y-%m-%d') for i in sorted(set(report_df['date']))]
|
|
774
|
+
|
|
775
|
+
report_dt_group = report_df.groupby('date')
|
|
776
|
+
|
|
777
|
+
for grp in report_dt_group.groups:
|
|
778
|
+
equity_history.append(equity_history[-1]+sum(list(report_dt_group.get_group(grp)['pnl'])))
|
|
779
|
+
|
|
780
|
+
# for pl in report_df['pnl']:
|
|
781
|
+
# equity_history.append(equity_history[-1] + pl)
|
|
782
|
+
|
|
783
|
+
mt_dict['equity_history'] = list(zip(eq_dates, equity_history))
|
|
784
|
+
# mt_dict['equity_history'] = list(zip(dates, report_pnl))
|
|
785
|
+
|
|
786
|
+
# Calculating Absolute Drawdown
|
|
787
|
+
min_drawdown = round(equity_history[0] - min(equity_history), 2)
|
|
788
|
+
mt_dict['abs_drawdown'] = min_drawdown
|
|
789
|
+
|
|
790
|
+
diff_points = list()
|
|
791
|
+
|
|
792
|
+
if len(equity_history) >= 2:
|
|
793
|
+
for prev_idx, curr_balance in enumerate(equity_history[1:], start=0):
|
|
794
|
+
prev_balance = equity_history[prev_idx]
|
|
795
|
+
# If the next balance is less, then only check that
|
|
796
|
+
if prev_balance >= curr_balance:
|
|
797
|
+
difference = prev_balance - curr_balance
|
|
798
|
+
diff_points.append([prev_balance, curr_balance, difference])
|
|
799
|
+
|
|
800
|
+
if diff_points:
|
|
801
|
+
difference_li = [val[-1] for val in diff_points]
|
|
802
|
+
relative_points = [val[2] / val[0] for val in diff_points]
|
|
803
|
+
|
|
804
|
+
max_drawdown = max(difference_li)
|
|
805
|
+
max_drawdown_per = round((max_drawdown / diff_points[difference_li.index(max_drawdown)][0]) * 100, 2)
|
|
806
|
+
|
|
807
|
+
relative_drawdown = round(diff_points[relative_points.index(max(relative_points))][2], 2)
|
|
808
|
+
relative_drawdown_per = round(max(relative_points) * 100, 2)
|
|
809
|
+
else:
|
|
810
|
+
max_drawdown = 0
|
|
811
|
+
max_drawdown_per = 0
|
|
812
|
+
relative_drawdown = 0
|
|
813
|
+
relative_drawdown_per = 0
|
|
814
|
+
|
|
815
|
+
mt_dict['maximal_drawdown'] = max_drawdown
|
|
816
|
+
mt_dict['maximal_drawdown%'] = max_drawdown_per
|
|
817
|
+
|
|
818
|
+
mt_dict['relative_drawdown'] = relative_drawdown
|
|
819
|
+
mt_dict['relative_drawdown_per'] = relative_drawdown_per
|
|
820
|
+
|
|
821
|
+
mt_dict['total_trades'] = len(report_df)
|
|
822
|
+
|
|
823
|
+
signal_cnts = report_df['trade_type'].value_counts().to_dict()
|
|
824
|
+
|
|
825
|
+
if "LONG" not in signal_cnts:
|
|
826
|
+
signal_cnts['LONG'] = 0
|
|
827
|
+
long_pos_win = 0
|
|
828
|
+
else:
|
|
829
|
+
long_pos_win = round((len(report_df[report_df['trade_type'] == "LONG"][report_df['pnl'] > 0]) / len(report_df) * 100), 2)
|
|
830
|
+
|
|
831
|
+
if "SHORT" not in signal_cnts:
|
|
832
|
+
signal_cnts['SHORT'] = 0
|
|
833
|
+
short_pos_win = 0
|
|
834
|
+
else:
|
|
835
|
+
short_pos_win = round((len(report_df[report_df['trade_type'] == "SHORT"][report_df['pnl'] > 0]) / len(report_df) * 100), 2)
|
|
836
|
+
|
|
837
|
+
# # Short Position (Won%)
|
|
838
|
+
mt_dict['short_position'] = signal_cnts['SHORT']
|
|
839
|
+
mt_dict['short_position_won%'] = short_pos_win
|
|
840
|
+
|
|
841
|
+
# # Long Position (Won%)
|
|
842
|
+
mt_dict['long_position'] = signal_cnts['LONG']
|
|
843
|
+
mt_dict['long_position_won%'] = long_pos_win
|
|
844
|
+
|
|
845
|
+
# # Profit trades %
|
|
846
|
+
mt_dict['profit_trades_num'] = len(profit_trades)
|
|
847
|
+
mt_dict['profit_trades_num%'] = round((len(profit_trades) / len(report_df)) * 100, 2)
|
|
848
|
+
|
|
849
|
+
# # Loss trades %
|
|
850
|
+
mt_dict['loss_trades_num'] = len(loss_trades)
|
|
851
|
+
mt_dict['loss_trades_num%'] = round((len(loss_trades) / len(report_df)) * 100, 2)
|
|
852
|
+
|
|
853
|
+
# # The Largest profit trade
|
|
854
|
+
if profit_trades:
|
|
855
|
+
mt_dict['largest_profit_trade'] = max(profit_trades)
|
|
856
|
+
else:
|
|
857
|
+
mt_dict['largest_profit_trade'] = ""
|
|
858
|
+
|
|
859
|
+
# # The Largest loss trade
|
|
860
|
+
mt_dict['largest_loss_trade'] = min(loss_trades)
|
|
861
|
+
|
|
862
|
+
# # Average Profit trade
|
|
863
|
+
mt_dict['avg_profit_trade'] = round(gross_profit / len(profit_trades), 2)
|
|
864
|
+
|
|
865
|
+
# # Average Loss trade
|
|
866
|
+
mt_dict['avg_loss_trade'] = round(gross_loss / len(loss_trades), 2)
|
|
867
|
+
|
|
868
|
+
consecutive_out = [1 if pnl > 0 else -1 for pnl in report_df['pnl']]
|
|
869
|
+
cons_counts = [(x[0], len(list(x[1]))) for x in itertools.groupby(consecutive_out)]
|
|
870
|
+
|
|
871
|
+
# Get Sum of those consecutive series
|
|
872
|
+
final_cons_vals = []
|
|
873
|
+
profit_loss_values = report_df['pnl'].values.tolist()
|
|
874
|
+
st_idx = 0
|
|
875
|
+
for (cons_type, cons_cnt) in cons_counts:
|
|
876
|
+
final_cons_vals.append([cons_type, cons_cnt, profit_loss_values[st_idx: st_idx + cons_cnt]])
|
|
877
|
+
st_idx += cons_cnt
|
|
878
|
+
|
|
879
|
+
# Filter out the consecutive values
|
|
880
|
+
cons_counts = {1: [], -1: []}
|
|
881
|
+
|
|
882
|
+
for _ in final_cons_vals:
|
|
883
|
+
if _[0] > 0:
|
|
884
|
+
cons_counts[1].append(_)
|
|
885
|
+
else:
|
|
886
|
+
cons_counts[-1].append(_)
|
|
887
|
+
# {cons_counts[1].append(_) if _[0] > 0 else cons_counts[-1].append(_) for _ in final_cons_vals}
|
|
888
|
+
|
|
889
|
+
# # Maximum consecutive wins
|
|
890
|
+
if len(cons_counts[1]) > 0:
|
|
891
|
+
mt_dict['max_cons_wins_cnt'] = max(cons_counts[1], key=lambda x: x[1])[1]
|
|
892
|
+
|
|
893
|
+
# # Maximal consecutive profit
|
|
894
|
+
mt_dict['max_cons_wins_value'] = sum(max(cons_counts[1], key=lambda x: x[1])[2])
|
|
895
|
+
|
|
896
|
+
# # Average consecutive wins
|
|
897
|
+
mt_dict['avg_cons_wins'] = sum([val[1] for val in cons_counts[1]]) / len(cons_counts[1])
|
|
898
|
+
else:
|
|
899
|
+
mt_dict['max_cons_wins_cnt'] = 0
|
|
900
|
+
mt_dict['max_cons_wins_value'] = 0
|
|
901
|
+
mt_dict['avg_cons_wins'] = 0
|
|
902
|
+
|
|
903
|
+
if len(cons_counts[-1]) > 0:
|
|
904
|
+
# # Maximum consecutive loss
|
|
905
|
+
mt_dict['max_cons_loss_cnt'] = max(cons_counts[-1], key=lambda x: x[1])[1]
|
|
906
|
+
|
|
907
|
+
# # Maximal consecutive loss
|
|
908
|
+
mt_dict['max_cons_loss_value'] = sum(max(cons_counts[-1], key=lambda x: x[1])[2])
|
|
909
|
+
|
|
910
|
+
# # Average consecutive loss
|
|
911
|
+
mt_dict['avg_cons_loss'] = sum([val[1] for val in cons_counts[-1]]) / len(cons_counts[-1])
|
|
912
|
+
else:
|
|
913
|
+
# # Maximum consecutive loss
|
|
914
|
+
mt_dict['max_cons_loss_cnt'] = 0
|
|
915
|
+
mt_dict['max_cons_loss_value'] = 0
|
|
916
|
+
mt_dict['avg_cons_loss'] = 0
|
|
917
|
+
|
|
918
|
+
for k, v in mt_dict.items():
|
|
919
|
+
if type(v) in [np.float64, float]:
|
|
920
|
+
mt_dict[k] = round(v, 2)
|
|
921
|
+
|
|
922
|
+
return mt_dict
|
|
923
|
+
|
|
924
|
+
except Exception as e:
|
|
925
|
+
log_message = f"Exception in creating mt_report.\n{e}"
|
|
926
|
+
print(log_message)
|
|
927
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message, log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
928
|
+
return {}
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def find_last_trading_date(trade_collection):
|
|
932
|
+
"""Function to Fetch Last Trading Date"""
|
|
933
|
+
last_trading_date = None
|
|
934
|
+
try:
|
|
935
|
+
last_trading_date = list(trade_collection.find({'symbol': 'NIFTY 50'}).sort('date', -1).limit(1))[0]['date']
|
|
936
|
+
return last_trading_date
|
|
937
|
+
except Exception as e:
|
|
938
|
+
print("Exception in fetching last trading date : {}".format(e))
|
|
939
|
+
return last_trading_date
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
def mg_insert_log(collection, file_name, log_level, log_message, request_id=None, strategy_id=None, user_id=None, log_traceback=None, api_route=None):
|
|
943
|
+
""" Function to Insert Logs in MongoDB Logs"""
|
|
944
|
+
log_dict = {'api_route': api_route, 'request_id': request_id, 'strategy_id': strategy_id, 'user_id': user_id,
|
|
945
|
+
'file_name': file_name, 'log_level': log_level.upper(), 'timestamp': datetime.datetime.now(),
|
|
946
|
+
'message': log_message, 'traceback': log_traceback}
|
|
947
|
+
|
|
948
|
+
if not request_id:
|
|
949
|
+
del log_dict['request_id']
|
|
950
|
+
if not strategy_id:
|
|
951
|
+
del log_dict['strategy_id']
|
|
952
|
+
if not user_id:
|
|
953
|
+
del log_dict['user_id']
|
|
954
|
+
if not log_traceback:
|
|
955
|
+
del log_dict['traceback']
|
|
956
|
+
if not api_route:
|
|
957
|
+
del log_dict['api_route']
|
|
958
|
+
|
|
959
|
+
collection.insert_one(log_dict)
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
def dates_list_from_start_end(start, end):
|
|
963
|
+
delta = end - start
|
|
964
|
+
days = [start + datetime.timedelta(days=i) for i in range(delta.days + 1)]
|
|
965
|
+
return days
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
def trading_dates_from_given_dates(st_date, en_date, db_collection, exchange='NSE', file_name=None, logs_coll=None):
|
|
969
|
+
try:
|
|
970
|
+
if exchange == 'NSE':
|
|
971
|
+
symbol = 'NIFTY 50'
|
|
972
|
+
else:
|
|
973
|
+
symbol = 'NIFTY 50'
|
|
974
|
+
|
|
975
|
+
trading_dates = db_collection.find({'symbol': symbol, 'date': {'$gte': st_date, '$lte': en_date}}).distinct('date')
|
|
976
|
+
except Exception as e:
|
|
977
|
+
log_message = f"Could not find TradingDates using database.\nReturning date list from given start date and end date.\n{e}\n"
|
|
978
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message,
|
|
979
|
+
log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
980
|
+
|
|
981
|
+
trading_dates = dates_list_from_start_end(st_date, en_date)
|
|
982
|
+
|
|
983
|
+
return trading_dates
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
def gnl_on_dates_on_given_time(dates_list, gnl_time, symbols_list, db_collection, gnl_stocks_length, file_name, logs_coll):
|
|
987
|
+
gnl_dict = dict()
|
|
988
|
+
top_gnl_length = int(gnl_stocks_length // 2)
|
|
989
|
+
for dt in dates_list:
|
|
990
|
+
try:
|
|
991
|
+
timestamp = datetime.datetime.combine(dt.date(), gnl_time)
|
|
992
|
+
data_li = list(db_collection.find({'symbol': {'$in': symbols_list}, 'timestamp': timestamp}))
|
|
993
|
+
|
|
994
|
+
change_dict_at_given_time = dict()
|
|
995
|
+
for data in data_li:
|
|
996
|
+
symbol = data['symbol']
|
|
997
|
+
try:
|
|
998
|
+
this_close = data['close']
|
|
999
|
+
previous_day_close = data['previous_day_close']
|
|
1000
|
+
percentage_change = ((this_close - previous_day_close) / previous_day_close) * 100
|
|
1001
|
+
change_dict_at_given_time[symbol] = percentage_change
|
|
1002
|
+
except Exception as e:
|
|
1003
|
+
log_message = f"{symbol}: {dt}\n{e}\n"
|
|
1004
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message,
|
|
1005
|
+
log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
1006
|
+
continue
|
|
1007
|
+
|
|
1008
|
+
change_sorted = sorted(change_dict_at_given_time.items(), key=lambda x: x[1], reverse=True)
|
|
1009
|
+
gainers = [x[0] for x in change_sorted[:top_gnl_length]]
|
|
1010
|
+
losers = [x[0] for x in change_sorted[top_gnl_length.__neg__():]]
|
|
1011
|
+
gnl_dict[dt] = {'gainers': gainers, 'losers': losers}
|
|
1012
|
+
|
|
1013
|
+
except Exception as e:
|
|
1014
|
+
log_message = f"Exception in getting GnL Dict for date: {dt}\n{e}\n"
|
|
1015
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message,
|
|
1016
|
+
log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
1017
|
+
continue
|
|
1018
|
+
|
|
1019
|
+
return gnl_dict
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
def get_fibonacci_pivot_points(high, low, close, level1=0.382, level2=0.618, level3=1.0, file_name=None, logs_coll=None):
|
|
1023
|
+
""" Function to get previous date ohlc and then apply fibonacci pivot points """
|
|
1024
|
+
'''
|
|
1025
|
+
Formula:
|
|
1026
|
+
- pivot = (h + l + c) / 3 # variants duplicate close or add open
|
|
1027
|
+
- support1 = p - level1 * (high - low) # level1 0.382
|
|
1028
|
+
- support2 = p - level2 * (high - low) # level2 0.618
|
|
1029
|
+
- support3 = p - level3 * (high - low) # level3 1.000
|
|
1030
|
+
- resistance1 = p + level1 * (high - low) # level1 0.382
|
|
1031
|
+
- resistance2 = p + level2 * (high - low) # level2 0.618
|
|
1032
|
+
- resistance3 = p + level3 * (high - low) # level3 1.000
|
|
1033
|
+
'''
|
|
1034
|
+
|
|
1035
|
+
try:
|
|
1036
|
+
candle_length = abs(high - low)
|
|
1037
|
+
|
|
1038
|
+
p = round((high + low + close) / 3, 2)
|
|
1039
|
+
|
|
1040
|
+
r1 = round(p + level1 * candle_length, 2)
|
|
1041
|
+
r2 = round(p + level2 * candle_length, 2)
|
|
1042
|
+
r3 = round(p + level3 * candle_length, 2)
|
|
1043
|
+
|
|
1044
|
+
s1 = round(p - level1 * candle_length, 2)
|
|
1045
|
+
s2 = round(p - level2 * candle_length, 2)
|
|
1046
|
+
s3 = round(p - level3 * candle_length, 2)
|
|
1047
|
+
except Exception as e:
|
|
1048
|
+
log_message = f"Exception in calculating Fibonacci Pivot points.\nReturning None\n{e}\n"
|
|
1049
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message,
|
|
1050
|
+
log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
1051
|
+
return None, None, None, None, None, None, None
|
|
1052
|
+
|
|
1053
|
+
return p, r1, r2, r3, s1, s2, s3
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
def get_lot_size(stocks_li, exchange='NSE', segment='future', kite_api_key=None, file_name=None, logs_coll=None):
|
|
1057
|
+
lot_size_dict = dict()
|
|
1058
|
+
try:
|
|
1059
|
+
if exchange == 'NSE':
|
|
1060
|
+
from kiteconnect import KiteConnect
|
|
1061
|
+
kite_obj = KiteConnect(kite_api_key)
|
|
1062
|
+
stocks_df = pd.DataFrame(kite_obj.instruments(exchange='NFO'))
|
|
1063
|
+
if segment == 'future':
|
|
1064
|
+
fut_df = stocks_df[stocks_df['instrument_type'] == 'FUT']
|
|
1065
|
+
|
|
1066
|
+
for symbol in stocks_li:
|
|
1067
|
+
try:
|
|
1068
|
+
if symbol == 'NIFTY 50':
|
|
1069
|
+
symbol = 'NIFTY'
|
|
1070
|
+
if symbol == 'NIFTY BANK':
|
|
1071
|
+
symbol = 'BANKNIFTY'
|
|
1072
|
+
|
|
1073
|
+
stock_df = fut_df[fut_df['name'] == symbol]
|
|
1074
|
+
lot_size = list(stock_df['lot_size'])[0]
|
|
1075
|
+
|
|
1076
|
+
if symbol == 'NIFTY':
|
|
1077
|
+
symbol = 'NIFTY 50'
|
|
1078
|
+
if symbol == 'BANKNIFTY':
|
|
1079
|
+
symbol = 'NIFTY BANK'
|
|
1080
|
+
|
|
1081
|
+
lot_size_dict[symbol] = lot_size
|
|
1082
|
+
except Exception as e:
|
|
1083
|
+
lot_size_dict[symbol] = 1
|
|
1084
|
+
log_message = f"Exception in getting lot size of {symbol}.\nSetting lot_size of this symbol to 1\n{e}\n"
|
|
1085
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message,
|
|
1086
|
+
log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
1087
|
+
|
|
1088
|
+
except Exception as e:
|
|
1089
|
+
log_message = f"Exception in get_lot_size function\n{e}\n"
|
|
1090
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message,
|
|
1091
|
+
log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
1092
|
+
|
|
1093
|
+
return lot_size_dict
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
def near_value(trade_type, operation, current_value, near_pct, file_name, logs_coll):
|
|
1097
|
+
value = current_value
|
|
1098
|
+
try:
|
|
1099
|
+
near_pct /= 100
|
|
1100
|
+
if trade_type == 'LONG':
|
|
1101
|
+
if operation == 'ENTRY':
|
|
1102
|
+
value = current_value * (1 + near_pct)
|
|
1103
|
+
elif operation == 'EXIT':
|
|
1104
|
+
value = current_value * (1 - near_pct)
|
|
1105
|
+
|
|
1106
|
+
elif trade_type == 'SHORT':
|
|
1107
|
+
if operation == 'ENTRY':
|
|
1108
|
+
value = current_value * (1 - near_pct)
|
|
1109
|
+
elif operation == 'EXIT':
|
|
1110
|
+
value = current_value * (1 + near_pct)
|
|
1111
|
+
else:
|
|
1112
|
+
value = current_value
|
|
1113
|
+
log_message = f"trade type does not match with 'LONG' or 'SHORT'"
|
|
1114
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message)
|
|
1115
|
+
except Exception as e:
|
|
1116
|
+
log_message = f"Exception in finding near value.\nReturning same value as given\n{e}\n"
|
|
1117
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message,
|
|
1118
|
+
log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
1119
|
+
value = current_value
|
|
1120
|
+
|
|
1121
|
+
return value
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
def get_pnl(trade_type, entry_p, exit_p, lot_size, file_name, logs_coll):
|
|
1125
|
+
try:
|
|
1126
|
+
if trade_type == 'LONG':
|
|
1127
|
+
points_gain = (exit_p - entry_p)
|
|
1128
|
+
pnl = points_gain * lot_size
|
|
1129
|
+
|
|
1130
|
+
elif trade_type == 'SHORT':
|
|
1131
|
+
points_gain = (entry_p - exit_p)
|
|
1132
|
+
pnl = points_gain * lot_size
|
|
1133
|
+
|
|
1134
|
+
else:
|
|
1135
|
+
points_gain = "Error in points gain calculation"
|
|
1136
|
+
pnl = 'Error in pnl calculation'
|
|
1137
|
+
|
|
1138
|
+
return points_gain, pnl
|
|
1139
|
+
except Exception as e:
|
|
1140
|
+
log_message = f"Could not calculate pnl using get_pnl function."
|
|
1141
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message,
|
|
1142
|
+
log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
1143
|
+
return 0, 0
|
|
1144
|
+
|
|
1145
|
+
|
|
1146
|
+
def calculate_margin(price, lot_sizes):
|
|
1147
|
+
""" Function to calculate future margin of Top GL """
|
|
1148
|
+
future_margin = 0
|
|
1149
|
+
margin = {}
|
|
1150
|
+
for stock in price:
|
|
1151
|
+
margin[stock] = price[stock] * lot_sizes[stock]
|
|
1152
|
+
future_margin += price[stock] * lot_sizes[stock]
|
|
1153
|
+
|
|
1154
|
+
return future_margin,margin
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
def calculate_margin_percent(price, margin, lot_sizes):
|
|
1158
|
+
""" Function to calculate margin percentage """
|
|
1159
|
+
margin_percent = {}
|
|
1160
|
+
for stock in price:
|
|
1161
|
+
percent = (price[stock]*lot_sizes[stock])* (100/margin)
|
|
1162
|
+
margin_percent[stock] = percent
|
|
1163
|
+
return margin_percent
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
def calculate_amount_distribution(percent_distribution, investment_amount):
|
|
1167
|
+
""" Function to calculate Investment Amount using percentage distribution """
|
|
1168
|
+
amount_distribution = {}
|
|
1169
|
+
for stock in percent_distribution:
|
|
1170
|
+
amount_distribution[stock] = investment_amount * percent_distribution[stock]/100
|
|
1171
|
+
return amount_distribution
|
|
1172
|
+
|
|
1173
|
+
|
|
1174
|
+
def calculate_share_distribution(price, amount_distribution, file_name, logs_coll):
|
|
1175
|
+
""" Function to calculate share distribution of Top GL Stocks """
|
|
1176
|
+
share_distribution = {}
|
|
1177
|
+
for stock in price:
|
|
1178
|
+
try:
|
|
1179
|
+
_shares = round(amount_distribution[stock] / price[stock])
|
|
1180
|
+
if _shares <= 0:
|
|
1181
|
+
_shares = 1
|
|
1182
|
+
share_distribution[stock] = _shares
|
|
1183
|
+
except Exception as e:
|
|
1184
|
+
log_message = f"Error in calculating share_distribution - {stock} {amount_distribution} {price}"
|
|
1185
|
+
mg_insert_log(logs_coll, file_name, 'ERROR', log_message, log_traceback=''.join(tb.format_exception(None, e, e.__traceback__)))
|
|
1186
|
+
share_distribution[stock] = 1
|
|
1187
|
+
return share_distribution
|
|
1188
|
+
|
|
1189
|
+
|
|
1190
|
+
def calculate_distribution(price, lot_sizes, investment_amount):
|
|
1191
|
+
""" Function to calculate number of shares to buy/sell """
|
|
1192
|
+
|
|
1193
|
+
future_margin, margin = calculate_margin(price, lot_sizes)
|
|
1194
|
+
margin_percent = calculate_margin_percent(price, future_margin, lot_sizes)
|
|
1195
|
+
amount_distribution = calculate_amount_distribution(margin_percent, investment_amount)
|
|
1196
|
+
# share_distribution = calculate_share_distribution(price, amount_distribution)
|
|
1197
|
+
|
|
1198
|
+
return future_margin, margin_percent, amount_distribution #, share_distribution
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
def fetch_available_funds(credential_id):
|
|
1202
|
+
"""Function to fetch current available funds in the account"""
|
|
1203
|
+
try:
|
|
1204
|
+
print("***** Fetching Available Funds for : {} *****".format(credential_id))
|
|
1205
|
+
response = requests.get(orders_url+"get_broker_fund?credential_id={}".format(credential_id))
|
|
1206
|
+
response_dict = response.json()
|
|
1207
|
+
if response_dict["status"] == "success" and response_dict["message"] == "Fund Found":
|
|
1208
|
+
fund_available = response_dict["data"]["net_balance"]
|
|
1209
|
+
print("fund_available : {}".format(fund_available))
|
|
1210
|
+
return fund_available
|
|
1211
|
+
except Exception as e:
|
|
1212
|
+
print("Exception in Fetching Available Funds : {}".format(e))
|
|
1213
|
+
return None
|
|
1214
|
+
|
|
1215
|
+
|
|
1216
|
+
def calculate_funds(investment, available_funds):
|
|
1217
|
+
""" Function to calculate Funds required while placing order """
|
|
1218
|
+
try:
|
|
1219
|
+
print("Calculating Funds Required")
|
|
1220
|
+
if investment < available_funds:
|
|
1221
|
+
return investment
|
|
1222
|
+
else:
|
|
1223
|
+
return available_funds
|
|
1224
|
+
|
|
1225
|
+
except Exception as e:
|
|
1226
|
+
print("Exception in calculating funds : {}".format(e))
|
|
1227
|
+
pass
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def round_to_tick_multiple(input_price):
|
|
1231
|
+
"""Function to round price to nearest tick multiple"""
|
|
1232
|
+
try:
|
|
1233
|
+
rounded_price = round((math.ceil(input_price * 20) / 20), 2)
|
|
1234
|
+
# print("price : {} | rounded_price : {}".format(input_price, rounded_price))
|
|
1235
|
+
return rounded_price
|
|
1236
|
+
except Exception as e:
|
|
1237
|
+
print("Exception in round to tick multiple : {}".format(e))
|
|
1238
|
+
pass
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
def send_order_alert(alert_dict):
|
|
1242
|
+
"""Function to Send Alert on Order"""
|
|
1243
|
+
try:
|
|
1244
|
+
print("Sending Alert")
|
|
1245
|
+
response = requests.post(url=orders_url+"send_notification", json=alert_dict)
|
|
1246
|
+
print("response : {}".format(response.json()))
|
|
1247
|
+
except Exception as e:
|
|
1248
|
+
print("Exception in sending alert : {}".format(e))
|
|
1249
|
+
pass
|
|
1250
|
+
|
|
1251
|
+
|
|
1252
|
+
def check_order_status(credential_id, order_id, exchange):
|
|
1253
|
+
"""Function to fetch current order status"""
|
|
1254
|
+
try:
|
|
1255
|
+
print("Fetching Order Status")
|
|
1256
|
+
get_order_params = {'order_id': order_id, "exchange": exchange}
|
|
1257
|
+
last_order = requests.post(url=orders_url+"get_order_by_id", params={"credential_id": credential_id, 'order_details': json.dumps(get_order_params)}).json()
|
|
1258
|
+
print("order_status_response : {}".format(last_order))
|
|
1259
|
+
if last_order["status"] == "COMPLETE":
|
|
1260
|
+
last_order["exchange_timestamp"] = datetime.datetime.strptime(str(last_order["exchange_timestamp"]), '%Y-%m-%d %H:%M:%S')
|
|
1261
|
+
return "success", last_order
|
|
1262
|
+
|
|
1263
|
+
elif last_order["status"] == "CANCELLED":
|
|
1264
|
+
return "cancelled", None
|
|
1265
|
+
else:
|
|
1266
|
+
return "failed", None
|
|
1267
|
+
except Exception as e:
|
|
1268
|
+
print("Exception in Check Order Status Function : {}".format(e))
|
|
1269
|
+
return "failed", None
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
def calculate_brokerage(buy_price, sell_price, quantity, broker="zerodha", market_type="options"):
|
|
1273
|
+
"""Function to calculate brokerage"""
|
|
1274
|
+
try:
|
|
1275
|
+
if broker in ["zerodha", "dhan", "icici direct"]:
|
|
1276
|
+
|
|
1277
|
+
gst = 0.18
|
|
1278
|
+
sebi_charges = 0.000001
|
|
1279
|
+
clearing_charge = 0
|
|
1280
|
+
|
|
1281
|
+
if market_type == "options":
|
|
1282
|
+
stt_charges = 0.0625
|
|
1283
|
+
exchange_charges = 0.0505
|
|
1284
|
+
stampduty_charges = 0.00003
|
|
1285
|
+
_kite_brokerage = 40
|
|
1286
|
+
else:
|
|
1287
|
+
stt_charges = 0.01
|
|
1288
|
+
exchange_charges = 0.002
|
|
1289
|
+
stampduty_charges = 0.00002
|
|
1290
|
+
_kite_brokerage = 0.03
|
|
1291
|
+
|
|
1292
|
+
# Calculating Turn Over
|
|
1293
|
+
turnover = (buy_price * quantity) + (sell_price * quantity)
|
|
1294
|
+
|
|
1295
|
+
# Calculating Brokerage
|
|
1296
|
+
if market_type=="options":
|
|
1297
|
+
brokerage = _kite_brokerage
|
|
1298
|
+
else:
|
|
1299
|
+
brokerage = round(turnover * (_kite_brokerage / 100), 2)
|
|
1300
|
+
brokerage = min(40, brokerage)
|
|
1301
|
+
|
|
1302
|
+
# Calculating STT
|
|
1303
|
+
stt = round(quantity * sell_price * stt_charges / 100)
|
|
1304
|
+
|
|
1305
|
+
# Calculating Exchange Transaction Charges
|
|
1306
|
+
exchange_charges = round((turnover / 100) * exchange_charges, 2)
|
|
1307
|
+
|
|
1308
|
+
# Calculating Sebi Charges
|
|
1309
|
+
sebi_charges = round(turnover * sebi_charges, 2)
|
|
1310
|
+
|
|
1311
|
+
# Calculating Stamp Duty
|
|
1312
|
+
stamp_duty = round(quantity * buy_price * stampduty_charges)
|
|
1313
|
+
|
|
1314
|
+
total_charges = brokerage + sebi_charges + exchange_charges
|
|
1315
|
+
|
|
1316
|
+
# Total GST Charges
|
|
1317
|
+
gst = round(total_charges * gst, 2)
|
|
1318
|
+
|
|
1319
|
+
# Calculating Total tax and Charges
|
|
1320
|
+
total_charges = round((brokerage + sebi_charges + exchange_charges + stt + clearing_charge + stamp_duty + gst), 2)
|
|
1321
|
+
|
|
1322
|
+
# Calculating Net PnL
|
|
1323
|
+
net_pl = ((sell_price - buy_price) * quantity) - total_charges
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
return total_charges, net_pl
|
|
1327
|
+
|
|
1328
|
+
elif broker == "kotak":
|
|
1329
|
+
gst = 0.18
|
|
1330
|
+
sebi_charges = 0.000001
|
|
1331
|
+
clearing_charge = 0
|
|
1332
|
+
brokerage = 0
|
|
1333
|
+
|
|
1334
|
+
if market_type == "options":
|
|
1335
|
+
stt_charges = 0.0625
|
|
1336
|
+
exchange_charges = 0.0505
|
|
1337
|
+
stampduty_charges = 0.00003
|
|
1338
|
+
else:
|
|
1339
|
+
stt_charges = 0.01
|
|
1340
|
+
exchange_charges = 0.002
|
|
1341
|
+
stampduty_charges = 0.00002
|
|
1342
|
+
|
|
1343
|
+
# Calculating Turn Over
|
|
1344
|
+
turnover = (buy_price * quantity) + (sell_price * quantity)
|
|
1345
|
+
|
|
1346
|
+
# Calculating STT
|
|
1347
|
+
stt = round(quantity * sell_price * stt_charges / 100)
|
|
1348
|
+
|
|
1349
|
+
# Calculating Exchange Transaction Charges
|
|
1350
|
+
exchange_charges = round((turnover / 100) * exchange_charges, 2)
|
|
1351
|
+
|
|
1352
|
+
# Calculating Sebi Charges
|
|
1353
|
+
sebi_charges = round(turnover * sebi_charges, 2)
|
|
1354
|
+
|
|
1355
|
+
# Calculating Stamp Duty
|
|
1356
|
+
stamp_duty = round(quantity * buy_price * stampduty_charges)
|
|
1357
|
+
|
|
1358
|
+
total_charges = brokerage + sebi_charges + exchange_charges
|
|
1359
|
+
|
|
1360
|
+
# Total GST Charges
|
|
1361
|
+
gst = round(total_charges * gst, 2)
|
|
1362
|
+
|
|
1363
|
+
# Calculating Total tax and Charges
|
|
1364
|
+
total_charges = round(
|
|
1365
|
+
(brokerage + sebi_charges + exchange_charges + stt + clearing_charge + stamp_duty + gst), 2)
|
|
1366
|
+
|
|
1367
|
+
# Calculating Net PnL
|
|
1368
|
+
net_pl = ((sell_price - buy_price) * quantity) - total_charges
|
|
1369
|
+
|
|
1370
|
+
return total_charges, net_pl
|
|
1371
|
+
except Exception as e:
|
|
1372
|
+
print("Exception in calculating brokerage : {}".format(e))
|
|
1373
|
+
pass
|
|
1374
|
+
|
|
1375
|
+
|
|
1376
|
+
def merge_candle(candle_1, candle_2):
|
|
1377
|
+
""" Function to merge input candles """
|
|
1378
|
+
try:
|
|
1379
|
+
output_candle = candle_1.copy()
|
|
1380
|
+
output_candle["high"] = max([candle_1["high"], candle_2["high"]])
|
|
1381
|
+
output_candle["low"] = min([candle_1["low"], candle_2["low"]])
|
|
1382
|
+
output_candle["timestamp"] = candle_2["timestamp"]
|
|
1383
|
+
if "volume" in candle_1:
|
|
1384
|
+
output_candle["volume"] = sum([candle_1["volume"], candle_2["volume"]])
|
|
1385
|
+
if "oi" in candle_1:
|
|
1386
|
+
output_candle["oi"] = sum([candle_1["oi"], candle_2["oi"]])
|
|
1387
|
+
return output_candle
|
|
1388
|
+
except Exception as e:
|
|
1389
|
+
print(f"Exception in merging candle : {e}")
|
|
1390
|
+
pass
|
|
1391
|
+
|
|
1392
|
+
|
|
1393
|
+
def convert_candle_pattern(dataframe, pattern):
|
|
1394
|
+
""" Function to convert a candle pattern """
|
|
1395
|
+
try:
|
|
1396
|
+
print("converting candle dataframe to : {}".format(pattern))
|
|
1397
|
+
if pattern == 'heikin_ashi':
|
|
1398
|
+
ha_df = dataframe.copy()
|
|
1399
|
+
ha_df['open'] = 0.0
|
|
1400
|
+
# Setting Close Values
|
|
1401
|
+
ha_df['close'] = (dataframe['open'] + dataframe['high'] + dataframe['low'] + dataframe['close']) / 4
|
|
1402
|
+
|
|
1403
|
+
for i in range(0, len(dataframe)):
|
|
1404
|
+
if i == 0:
|
|
1405
|
+
ha_df.loc[0, 'open'] = (dataframe['open'][i] + dataframe['close'][i]) / 2
|
|
1406
|
+
else:
|
|
1407
|
+
ha_df.loc[i, 'open'] = (ha_df.loc[i - 1, 'open'] + ha_df.loc[i - 1, 'close']) / 2
|
|
1408
|
+
|
|
1409
|
+
# ha_df.loc[i, 'ha_color'] = 'green' if ha_df['close'][i] > ha_df['open'][i] else 'red'
|
|
1410
|
+
|
|
1411
|
+
ha_df['high'] = ha_df[['high', 'open', 'close']].max(axis=1)
|
|
1412
|
+
ha_df['low'] = ha_df[['low', 'open', 'close']].min(axis=1)
|
|
1413
|
+
|
|
1414
|
+
return ha_df
|
|
1415
|
+
|
|
1416
|
+
if pattern == 'ask':
|
|
1417
|
+
last_candle = None
|
|
1418
|
+
output_list = []
|
|
1419
|
+
candles_list = dataframe.to_dict('records')
|
|
1420
|
+
|
|
1421
|
+
for c_index, candle in enumerate(candles_list):
|
|
1422
|
+
if c_index == 0:
|
|
1423
|
+
last_candle = candle
|
|
1424
|
+
else:
|
|
1425
|
+
# Finding Highest between Open and Close Price
|
|
1426
|
+
hp = max([last_candle["open"], last_candle["close"]])
|
|
1427
|
+
|
|
1428
|
+
# Finding Lowest between Open and Close Price
|
|
1429
|
+
lp = min([last_candle["open"], last_candle["close"]])
|
|
1430
|
+
|
|
1431
|
+
market_exit_time = datetime.datetime.strptime("15:20", '%H:%M').time()
|
|
1432
|
+
|
|
1433
|
+
if candle["timestamp"].time() < market_exit_time:
|
|
1434
|
+
# Checking Inside Body Candle condition
|
|
1435
|
+
if lp <= candle['open'] <= hp and lp <= candle['close'] <= hp:
|
|
1436
|
+
# Merging To Last Candle
|
|
1437
|
+
last_candle = merge_candle(candle_1=last_candle, candle_2=candle)
|
|
1438
|
+
else:
|
|
1439
|
+
output_list.append(last_candle)
|
|
1440
|
+
|
|
1441
|
+
# Reassigning Last Candle
|
|
1442
|
+
last_candle = candle
|
|
1443
|
+
else:
|
|
1444
|
+
output_list.append(candle)
|
|
1445
|
+
|
|
1446
|
+
ask_df = pd.DataFrame(output_list)
|
|
1447
|
+
return ask_df
|
|
1448
|
+
except Exception as e:
|
|
1449
|
+
print("exception in converting candle pattern : {}".format(e))
|
|
1450
|
+
pass
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
def nearest_expiry_option_data(option_candle_list):
|
|
1454
|
+
try:
|
|
1455
|
+
expiry_dates = sorted([datetime.datetime.strptime(candle['expiry'], "%Y-%m-%d") for candle in option_candle_list])
|
|
1456
|
+
expiry_find = datetime.datetime.strftime(expiry_dates[0], "%Y-%m-%d")
|
|
1457
|
+
option_candle_df = pd.DataFrame(option_candle_list)
|
|
1458
|
+
option_candle_df.drop_duplicates(inplace=True)
|
|
1459
|
+
option_candle_df = option_candle_df[option_candle_df['expiry'] == expiry_find]
|
|
1460
|
+
|
|
1461
|
+
return option_candle_df.to_dict('records')
|
|
1462
|
+
except Exception as e:
|
|
1463
|
+
print("error in filtering option data for nearest expiry")
|
|
1464
|
+
return []
|
|
1465
|
+
|