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/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
+