trendvisdata 1.0.4__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.
@@ -0,0 +1,785 @@
1
+ """
2
+ Generate trend strength fields
3
+
4
+ """
5
+ import copy
6
+ import warnings
7
+ import numpy as np
8
+ import pandas as pd
9
+ from technicalmethods.methods import Indicators
10
+
11
+ class Fields():
12
+ """
13
+ Create trend strength fields across range of tickers
14
+
15
+ """
16
+
17
+ @classmethod
18
+ def generate_fields(
19
+ cls,
20
+ params: dict,
21
+ ticker_dict: dict) -> dict:
22
+ """
23
+ Create and add various trend indicators to each DataFrame in
24
+ the dictionary of tickers
25
+
26
+ Parameters
27
+ ----------
28
+ params : Dict
29
+ ma_list : List, optional
30
+ List of Moving Average periods. The default is
31
+ [10, 20, 30, 50, 100, 200].
32
+ macd_params : List, optional
33
+ List of MACD parameters. The default is [12, 26, 9].
34
+ adx_list : List, optional
35
+ List of ADX periods. The default is [10, 20, 30, 50, 100, 200].
36
+ ma_cross_list : List, optional
37
+ List of Moving Average Crossover periods. The default is
38
+ [(10, 30), (20, 50), (50, 200)].
39
+ price_cross_list : List, optional
40
+ List of Moving Average Price Crossover periods. The default is
41
+ [10, 20, 30, 50, 100, 200].
42
+ rsi_list : List, optional
43
+ List of RSI periods. The default is [10, 20, 30, 50, 100, 200].
44
+ breakout_list : List, optional
45
+ List of breakout periods. The default is
46
+ [10, 20, 30, 50, 100, 200].
47
+ atr_list : List, optional
48
+ List of ATR periods. The default is [14].
49
+ ticker_dict : Dict
50
+ Dictionary of price history DataFrames, one for each ticker.
51
+
52
+ Returns
53
+ -------
54
+ ticker_dict : Dict
55
+ Dictionary of DataFrames of each ticker updated with additional
56
+ trend indicators.
57
+
58
+ """
59
+
60
+ with warnings.catch_warnings():
61
+ warnings.filterwarnings("error")
62
+
63
+ # Loop through each ticker in ticker_dict
64
+ for ticker, frame in ticker_dict.items():
65
+
66
+ # Create moving averages of 10, 20, 30, 50 and 200 day
67
+ # timeframes
68
+ frame = cls._field_ma(
69
+ params=params, frame=frame, ticker=ticker)
70
+
71
+ # Create flag for price crossing moving average
72
+ frame = cls._field_px_ma(
73
+ params=params, frame=frame, ticker=ticker)
74
+
75
+ # Create MACD, Signal and Hist using default parameters
76
+ # of 12, 26, 9
77
+ frame = cls._field_macd(
78
+ params=params, frame=frame, ticker=ticker)
79
+
80
+ # Create ADX of 14, 20, 50, 100 and 200 day timeframes
81
+ # Create flags for ADX over 25
82
+ frame = cls._field_adx(
83
+ params=params, frame=frame, ticker=ticker)
84
+
85
+ # Create flag for fast moving average crossing slow moving
86
+ # average
87
+ frame = cls._field_ma_cross(
88
+ params=params, frame=frame, ticker=ticker)
89
+
90
+ # Create RSI with 14, 20, 50, 100 and 200 day timeframes
91
+ # Create flag for RSI over 70 or under 30
92
+ frame = cls._field_rsi(
93
+ params=params, frame=frame, ticker=ticker)
94
+
95
+ # Create breakout flags with 14, 20, 50, 100 and 200 day
96
+ # timeframes
97
+ frame = cls._field_breakout(
98
+ params=params, frame=frame, ticker=ticker)
99
+
100
+ # Create Average True Range with 14 day timeframe
101
+ frame = cls._field_atr(
102
+ params=params, frame=frame, ticker=ticker)
103
+
104
+ # Create column showing the percentage change in closing price since the start
105
+ frame = cls._start_change(
106
+ params=params, frame=frame, ticker=ticker)
107
+
108
+
109
+ return ticker_dict
110
+
111
+
112
+ @staticmethod
113
+ def _field_ma(
114
+ params:dict,
115
+ frame: pd.DataFrame,
116
+ ticker: str) -> pd.DataFrame:
117
+
118
+ for tenor in params['ma_list']:
119
+ try:
120
+ frame['MA_'+str(tenor)] = frame['Close'].rolling(
121
+ window=str(tenor)+'D').mean()
122
+
123
+ except RuntimeWarning:
124
+ print("Error with "
125
+ + ticker
126
+ + " MA_"
127
+ + str(tenor)
128
+ + " : More than "
129
+ + str(tenor)
130
+ + " consecutive days unchanged price")
131
+
132
+ return frame
133
+
134
+
135
+ @staticmethod
136
+ def _field_px_ma(
137
+ params:dict,
138
+ frame: pd.DataFrame,
139
+ ticker: str) -> pd.DataFrame:
140
+
141
+ for tenor in params['price_cross_list']:
142
+ try:
143
+ frame['PX_MA_'+str(tenor)+'_flag'] = np.where(
144
+ frame['Close'] > frame['MA_'+str(tenor)], 1, -1)
145
+
146
+ except RuntimeWarning:
147
+ print("Error with "
148
+ + ticker
149
+ + " PX_MA_"
150
+ + str(tenor)
151
+ + " : More than "
152
+ + str(tenor)
153
+ + " consecutive days unchanged price")
154
+
155
+ return frame
156
+
157
+
158
+ @staticmethod
159
+ def _field_macd(
160
+ params:dict,
161
+ frame: pd.DataFrame,
162
+ ticker: str) -> pd.DataFrame:
163
+
164
+ try:
165
+ frame['MACD'], frame['MACD_SIGNAL'], \
166
+ frame['MACD_HIST'] = Indicators.MACD(
167
+ close=frame['Close'],
168
+ fast=params['macd_params'][0],
169
+ slow=params['macd_params'][1],
170
+ signal=params['macd_params'][2])
171
+
172
+ # Create flag for MACD histogram increasing
173
+ frame['MACD_flag'] = np.where(
174
+ frame['MACD_HIST'].diff() > 0, 1, -1)
175
+
176
+ except RuntimeWarning:
177
+ print("Error with " + ticker + " MACD")
178
+
179
+ return frame
180
+
181
+
182
+ @staticmethod
183
+ def _field_adx(
184
+ params:dict,
185
+ frame: pd.DataFrame,
186
+ ticker: str) -> pd.DataFrame:
187
+
188
+ for tenor in params['adx_list']:
189
+ try:
190
+ frame['ADX_'+str(tenor)] = Indicators.ADX(
191
+ high=frame['High'],
192
+ low=frame['Low'],
193
+ close=frame['Close'],
194
+ time_period=tenor)
195
+ frame['ADX_'+str(tenor)+'_flag'] = np.where(
196
+ frame['ADX_'+str(tenor)] > 25, np.where(
197
+ frame['PX_MA_'+str(tenor)+'_flag'] == 1, 1, -1), 0)
198
+
199
+ except RuntimeWarning:
200
+ print("Error with "
201
+ + ticker
202
+ + " ADX_"+str(tenor)
203
+ + " : More than "
204
+ + str(tenor)
205
+ + " consecutive days unchanged price")
206
+
207
+ except IndexError:
208
+ print("Error with "
209
+ + ticker
210
+ + " ADX_"+str(tenor)
211
+ + " Problem with EMA calculation")
212
+
213
+ return frame
214
+
215
+
216
+ @staticmethod
217
+ def _field_ma_cross(
218
+ params:dict,
219
+ frame: pd.DataFrame,
220
+ ticker: str) -> pd.DataFrame:
221
+
222
+ for tenor_pair in params['ma_cross_list']:
223
+ try:
224
+ frame['MA_'+str(tenor_pair[0])+'_'+str(
225
+ tenor_pair[1])+'_flag'] = np.where(
226
+ frame['MA_'+str(tenor_pair[0])] > frame[
227
+ 'MA_'+str(tenor_pair[1])], 1, -1)
228
+
229
+ except RuntimeWarning:
230
+ print("Error with " + ticker + " MA_"+str(tenor_pair))
231
+
232
+ return frame
233
+
234
+
235
+ @staticmethod
236
+ def _field_rsi(
237
+ params:dict,
238
+ frame: pd.DataFrame,
239
+ ticker: str) -> pd.DataFrame:
240
+
241
+ for tenor in params['rsi_list']:
242
+ try:
243
+ frame['RSI_'+str(tenor)] = Indicators.RSI(
244
+ close=frame['Close'], time_period=tenor)
245
+ frame['RSI_'+str(tenor)+'_flag'] = np.where(
246
+ frame['RSI_'+str(tenor)] > 70, 1, np.where(
247
+ frame['RSI_'+str(tenor)] < 30, -1, 0))
248
+
249
+ except RuntimeWarning:
250
+ print("Error with "
251
+ + ticker
252
+ + " RSI_"+str(tenor)
253
+ + " : More than "
254
+ + str(tenor)
255
+ + " consecutive days unchanged price")
256
+
257
+ return frame
258
+
259
+
260
+ @staticmethod
261
+ def _field_breakout(
262
+ params:dict,
263
+ frame: pd.DataFrame,
264
+ ticker: str) -> pd.DataFrame:
265
+
266
+ for tenor in params['breakout_list']:
267
+ try:
268
+ frame['low_'+str(tenor)], frame['high_'+str(tenor)], \
269
+ frame['breakout_'+str(
270
+ tenor)+'_flag'] = Indicators.breakout(
271
+ high=frame['High'], low=frame['Low'],
272
+ time_period=tenor)
273
+
274
+ except RuntimeWarning:
275
+ print("Error with "
276
+ + ticker
277
+ + " breakout_"
278
+ + str(tenor)
279
+ + " : More than "
280
+ + str(tenor)
281
+ + " consecutive days unchanged price")
282
+
283
+ return frame
284
+
285
+
286
+ @staticmethod
287
+ def _field_atr(
288
+ params:dict,
289
+ frame: pd.DataFrame,
290
+ ticker: str) -> pd.DataFrame:
291
+
292
+ for tenor in params['atr_list']:
293
+ try:
294
+ frame['ATR_'+str(tenor)] = Indicators.ATR(
295
+ high=frame['High'],
296
+ low=frame['Low'],
297
+ close=frame['Close'],
298
+ time_period=tenor)
299
+
300
+ except RuntimeWarning:
301
+ print("Error with "
302
+ + ticker
303
+ + " ATR_"
304
+ + str(tenor)
305
+ + " : More than "
306
+ + str(tenor)
307
+ + " consecutive days unchanged price")
308
+
309
+ return frame
310
+
311
+ @staticmethod
312
+ def _start_change(
313
+ params:dict,
314
+ frame: pd.DataFrame,
315
+ ticker: str) -> pd.DataFrame:
316
+
317
+ try:
318
+ frame['first_close'] = frame['Close'].iloc[0]
319
+ frame['start_change'] = frame['Close'] - frame['first_close']
320
+ frame['start_change_percent'] = frame[
321
+ 'start_change'] / frame['Close'].iloc[0] * 100
322
+
323
+
324
+ except RuntimeWarning:
325
+ print("Error with "
326
+ + ticker
327
+ + " percent change")
328
+
329
+ return frame
330
+
331
+
332
+
333
+ @classmethod
334
+ def generate_trend_strength(
335
+ cls,
336
+ params: dict,
337
+ ticker_dict: dict,
338
+ sector_mappings_df: pd.DataFrame) -> pd.DataFrame:
339
+ """
340
+ Create a DataFrame showing the strength of trend for selected
341
+ markets.
342
+
343
+ Parameters
344
+ ----------
345
+
346
+ params : Dict
347
+ trend_flags : List
348
+ The list of indicators used to calculate the strength of the
349
+ trend.
350
+ The default values are ['PX_MA_10',
351
+ 'ADX_10_flag',
352
+ 'RSI_10_flag',
353
+ 'breakout_10_flag',
354
+ 'MA_10_30',
355
+ 'MACD_flag',
356
+ 'PX_MA_20',
357
+ 'MA_20_50',
358
+ 'ADX_20_flag',
359
+ 'RSI_20_flag',
360
+ 'breakout_20_flag',
361
+ 'PX_MA_30',
362
+ 'ADX_30_flag',
363
+ 'RSI_30_flag',
364
+ 'breakout_30_flag',
365
+ 'PX_MA_50',
366
+ 'MA_50_200',
367
+ 'ADX_50_flag',
368
+ 'RSI_50_flag',
369
+ 'breakout_50_flag',
370
+ 'PX_MA_100',
371
+ 'ADX_100_flag',
372
+ 'RSI_100_flag',
373
+ 'breakout_100_flag',
374
+ 'PX_MA_200',
375
+ 'ADX_200_flag',
376
+ 'RSI_200_flag',
377
+ 'breakout_200_flag'
378
+ ].
379
+ ticker_dict : Dict
380
+ Dictionary of price history DataFrames, one for each ticker.
381
+ sector_mappings_df : DataFrame
382
+ Sector mappings DataFrame.
383
+
384
+ Returns
385
+ -------
386
+ barometer : DataFrame
387
+ DataFrame showing trend strength for each ticker.
388
+
389
+ """
390
+
391
+ # Prepare dataframe from ticker_dict
392
+ frame = cls._prep_frame(params, ticker_dict)
393
+
394
+ # Loop through each ticker in ticker_dict to populate trend
395
+ # flags in frame
396
+ for ticker, dataframe in ticker_dict.items():
397
+ for flag in params['trend_flags']:
398
+ try:
399
+ frame.loc[ticker, flag] = dataframe[flag].iloc[-1]
400
+
401
+ except KeyError:
402
+ print("Missing ticker: "
403
+ + ticker
404
+ + " , trend flag: "
405
+ + flag
406
+ + " from ticker dict")
407
+ frame.loc[ticker, flag] = 0
408
+
409
+ # Add column of largest change to filter out trending but largely static instruments
410
+ try:
411
+ minval = min(dataframe['start_change_percent'])
412
+ maxval = max(dataframe['start_change_percent'])
413
+ largest_change = max(abs(minval), maxval)
414
+ frame.loc[ticker, 'largest_change'] = largest_change
415
+
416
+ except KeyError:
417
+ print("Missing ticker: "
418
+ + ticker
419
+ + " , largest change")
420
+ frame.loc[ticker, 'largest_change'] = 0
421
+
422
+ # Create trend strength column that sums the trend flags and
423
+ # sort by this column
424
+ frame['Trend Strength'] = frame.iloc[:,1:30].sum(axis=1)
425
+ frame = frame.sort_values(by=['Trend Strength'], ascending=False)
426
+
427
+ # Create short name column, stripping text from longname
428
+ short_name = np.array(frame['Long_name'], dtype=str)
429
+ long_name = np.array(frame['Long_name'], dtype=str)
430
+
431
+ for row in range(0, len(frame['Long_name'])):
432
+ short_name[row] = long_name[row].partition(" Continuous")[0]
433
+
434
+ frame['Short_name'] = short_name
435
+
436
+ # Add column showing trend strength as Red, Amber, Green
437
+ barometer = frame.apply(cls._col_color, axis=1)
438
+
439
+ # Apply sector mappings
440
+ barometer = cls._barometer_sectors(
441
+ params, barometer, sector_mappings_df)
442
+
443
+ # barometer = cls._barometer_changes(
444
+ # barometer, ticker_dict)
445
+
446
+ return barometer
447
+
448
+
449
+ @staticmethod
450
+ def _prep_frame(
451
+ params: dict,
452
+ ticker_dict: dict) -> pd.DataFrame:
453
+
454
+ # Create list of tickers from ticker_dict
455
+ ticker_list = [ticker for ticker, df in ticker_dict.items()]
456
+
457
+ # Convert ticker_name_dict to DataFrame
458
+ ticker_name_df = pd.DataFrame.from_dict(
459
+ params['ticker_name_dict'], orient='index', columns=['Long_name'])
460
+
461
+ # Create empty DataFrame with Trend Flags as columns and
462
+ # tickers as rows
463
+ frame = pd.DataFrame(columns=params['trend_flags'], index=ticker_list)
464
+
465
+ # Merge the two DataFrames
466
+ frame = pd.merge(frame, ticker_name_df, left_index=True,
467
+ right_index=True)
468
+
469
+ # Reorder columns with list of column names moving the
470
+ # Long Name to the start
471
+ cols = [frame.columns[-1]] + [col for col in frame
472
+ if col != frame.columns[-1]]
473
+ frame = frame[cols]
474
+
475
+ return frame
476
+
477
+
478
+ @staticmethod
479
+ def _col_color(row: pd.Series) -> pd.Series:
480
+
481
+ # Create trend strength color column and absolute strength column
482
+ row['Absolute Trend Strength'] = np.abs(row['Trend Strength'])
483
+
484
+ if np.abs(row['Trend Strength']) < 5:
485
+ row['Trend Color'] = 'red'
486
+
487
+ elif np.abs(row['Trend Strength']) < 10:
488
+ row['Trend Color'] = 'orange'
489
+
490
+ else:
491
+ row['Trend Color'] = 'green'
492
+
493
+ return row
494
+
495
+
496
+ @staticmethod
497
+ def _barometer_sectors(
498
+ params: dict,
499
+ barometer: pd.DataFrame,
500
+ sector_mappings_df: pd.DataFrame) -> pd.DataFrame:
501
+ """
502
+ Add sector mappings to the trend barometer table to use in summary
503
+ graph
504
+
505
+ Parameters
506
+ ----------
507
+ params : Dict
508
+ Dictionary of key parameters.
509
+ sector_mappings_df : DataFrame
510
+ Sector mappings DataFrame.
511
+ barometer : DataFrame
512
+ DataFrame showing trend strength for each ticker.
513
+
514
+ Returns
515
+ -------
516
+ barometer : DataFrame
517
+ barometer updated with additional sector columns.
518
+
519
+ """
520
+
521
+ # Calculate the number of trend indicators
522
+ num_flags = len(params['trend_flags'])
523
+
524
+ # Join the baometer table to the sector mappings table
525
+ barometer = barometer.join(sector_mappings_df)
526
+
527
+ # Calculate Trend Strength as a percentage by dividing by the number
528
+ # of trend indicators
529
+ barometer['Trend Strength %'] = barometer[
530
+ 'Trend Strength'] / num_flags
531
+
532
+ # Calculate the absolute value of trend strength as a percentage
533
+ barometer['Absolute Trend Strength %'] = abs(
534
+ barometer['Trend Strength %'])
535
+
536
+ # Reset the index and rename column as 'Ticker
537
+ barometer = barometer.reset_index()
538
+ barometer = barometer.rename(columns={'index':'Ticker'})
539
+
540
+ # Add a column as the string 'Trend' to use in swarm plot
541
+ barometer['Trend'] = 'Trend'
542
+
543
+ return barometer
544
+
545
+
546
+ @staticmethod
547
+ def _barometer_changes(
548
+ barometer: pd.DataFrame,
549
+ ticker_dict: dict ) -> pd.DataFrame:
550
+
551
+ for ticker, dataframe in ticker_dict.items():
552
+ # Add column of largest change to filter out trending but largely static instruments
553
+ try:
554
+ minval = min(dataframe['start_change_percent'])
555
+ maxval = max(dataframe['start_change_percent'])
556
+ largest_change = max(abs(minval), maxval)
557
+ barometer.loc[ticker, 'largest_change'] = largest_change
558
+
559
+ except KeyError:
560
+ print("Missing ticker: "
561
+ + ticker
562
+ + " , largest change")
563
+ barometer.loc[ticker, 'largest_change'] = 0
564
+
565
+ return barometer
566
+
567
+
568
+ class TrendRank():
569
+ """
570
+ Generate list of strongly trending securities
571
+
572
+ """
573
+ @classmethod
574
+ def top_trend_calc(
575
+ cls,
576
+ tables: dict,
577
+ params: dict) -> tuple[dict, dict]:
578
+ """
579
+ Prepare list of top trending securities.
580
+
581
+ Parameters
582
+ ----------
583
+ barometer : DataFrame
584
+ DataFrame showing trend strength for each ticker.
585
+ norgate_source : Bool
586
+ Whether the market data source is norgate or yahoo.
587
+
588
+ Returns
589
+ -------
590
+ top_trends : Dict
591
+ Dictionary containing list and dictionary of most strongly
592
+ trending assets
593
+ ticker_dict : Dict
594
+ Dict of top trending securities.
595
+ ticker_list : List
596
+ List of top trending securities.
597
+ tables : Dict
598
+ Dictionary of key tables updated for the filtered barometer.
599
+
600
+ """
601
+ top_trends = {}
602
+
603
+ # Configure sector name
604
+ params = cls._set_sector_level_name(params)
605
+
606
+ tables['filtered_barometer'] = cls._filter_barometer(
607
+ tables, params)
608
+
609
+ tables = cls._sector_split(tables, params)
610
+ tables = cls._return_filter(tables)
611
+
612
+ top_trends['top_ticker_dict'] = {}
613
+ top_trends['top_ticker_list'] = []
614
+ for ticker in tables['filtered_barometer']['Ticker']:
615
+ if params['source'] == 'norgate':
616
+ ticker_mod = '&'+ticker.upper()
617
+ ticker_mod = ticker_mod.replace('&C_','&')
618
+ else:
619
+ ticker_mod = ticker
620
+ top_trends['top_ticker_dict'][tables['filtered_barometer'].index[
621
+ tables['filtered_barometer'][
622
+ 'Ticker'] == ticker][0] + 1] = (
623
+ ticker_mod, tables['raw_ticker_dict'][ticker][
624
+ ['Open', 'High', 'Low', 'Close']])
625
+
626
+ for rank, ticker in top_trends['top_ticker_dict'].items():
627
+ if rank < params['top_trend_params']['final_size'] + 1:
628
+ top_trends['top_ticker_list'].append(ticker)
629
+
630
+ return top_trends, tables
631
+
632
+
633
+ @staticmethod
634
+ def _set_sector_level_name(params):
635
+ if params['source'] == 'norgate':
636
+ params['sector_name'] = params[
637
+ 'commodity_sector_levels'][params['sector_level']-1]
638
+ params['returns_sector_name'] = params[
639
+ 'commodity_sector_levels'][params['returns_sector_level']-1]
640
+ # Otherwise for Yahoo SPX data
641
+ else:
642
+ params['sector_name'] = params[
643
+ 'equity_sector_levels'][params['sector_level']-1]
644
+ params['returns_sector_name'] = params[
645
+ 'equity_sector_levels'][params['returns_sector_level']-1]
646
+
647
+ return params
648
+
649
+
650
+ @classmethod
651
+ def _filter_barometer(
652
+ cls,
653
+ tables: dict,
654
+ params: dict) -> pd.DataFrame:
655
+ """
656
+ Sort and filter the top trending securities
657
+
658
+ Parameters
659
+ ----------
660
+ barometer : DataFrame
661
+ DataFrame showing trend strength for each ticker.
662
+ norgate_source : Bool
663
+ Whether the market data source is norgate or yahoo.
664
+
665
+ Returns
666
+ -------
667
+ filtered_barometer : DataFrame
668
+ DataFrame showing the top trending securities.
669
+
670
+ """
671
+ if params['source'] == 'norgate':
672
+ # Split the continuous futures data
673
+ tables = cls._futures_split(tables=tables, params=params)
674
+ barometer = copy.deepcopy(tables['futures_barometer'])
675
+ else:
676
+ barometer = copy.deepcopy(tables['barometer'])
677
+
678
+ data = barometer.sort_values(
679
+ by=['Absolute Trend Strength'],
680
+ ascending=False)[:params['top_trend_params']['initial_size']]
681
+
682
+ filtered_barometer = pd.DataFrame()
683
+
684
+ if params['source'] == 'norgate':
685
+ sectors = set(barometer['Mid Sector'])
686
+ data = data.sort_values(
687
+ by=['Mid Sector', 'Absolute Trend Strength'],
688
+ ascending=False)
689
+ else:
690
+ sectors = set(barometer['Sector'])
691
+ data = data.sort_values(
692
+ by=['Sector', 'Absolute Trend Strength'], ascending=False)
693
+
694
+ for sector in sectors:
695
+ if params['source'] == 'norgate':
696
+ frame = data[data['Mid Sector']==sector]
697
+ else:
698
+ frame = data[data['Sector']==sector]
699
+
700
+ if len(frame) > 0:
701
+ if params['source'] == 'norgate':
702
+ frame = frame.drop_duplicates(subset='Underlying')
703
+ else:
704
+ frame = frame.drop_duplicates(subset='Security')
705
+
706
+ if len(frame) > params['top_trend_params']['max_per_sector']:
707
+ frame = frame[:params['top_trend_params'][
708
+ 'max_per_sector']]
709
+ filtered_barometer = pd.concat([filtered_barometer, frame])
710
+
711
+ filtered_barometer = filtered_barometer.sort_values(
712
+ by=['Absolute Trend Strength'],
713
+ ascending=False).reset_index().drop(['index'], axis=1)
714
+
715
+ return filtered_barometer
716
+
717
+
718
+ @staticmethod
719
+ def _futures_split(
720
+ tables: dict,
721
+ params: dict) -> dict:
722
+ """
723
+ Split the futures from the rest of the norgate data (choosing
724
+ backadjusted data or not)
725
+
726
+ Parameters
727
+ ----------
728
+ tables : Dict
729
+ Dictionary of key tables.
730
+ params : Dict
731
+ Dictionary of key parameters
732
+
733
+ Returns
734
+ -------
735
+ tables : Dict
736
+ Dictionary of key tables updated for futures_ticker_dict and
737
+ futures_barometer.
738
+
739
+ """
740
+ barometer = copy.deepcopy(tables['barometer'])
741
+
742
+ # Select backadjusted futures
743
+ if params['tickers_adjusted']:
744
+ tables['futures_ticker_dict'] = {
745
+ k:v for k,v in tables['raw_ticker_dict'].items()
746
+ if '_ccb' in k}
747
+ tables['futures_barometer'] = barometer[
748
+ barometer['Ticker'].str.lower().str.contains('_ccb')]
749
+
750
+ # Select unadjusted futures
751
+ else:
752
+ tables['futures_ticker_dict'] = {
753
+ k:v for k,v in tables['raw_ticker_dict'].items()
754
+ if k.startswith('c_')}
755
+ tables['futures_barometer'] = barometer[
756
+ barometer['Ticker'].str.lower().str.startswith('c_')]
757
+
758
+ return tables
759
+
760
+
761
+ @staticmethod
762
+ def _sector_split(tables: dict, params: dict) -> dict:
763
+
764
+ barometer = copy.deepcopy(tables['barometer'])
765
+
766
+ tables['sectors'] = {}
767
+ sector_level = params['returns_sector_name']
768
+
769
+ sectors = set(barometer[sector_level])
770
+
771
+ for sector in sectors:
772
+ tables['sectors'][sector] = tables[
773
+ 'barometer'].loc[barometer[sector_level] == sector]
774
+
775
+ return tables
776
+
777
+
778
+ @staticmethod
779
+ def _return_filter(tables: dict) -> dict:
780
+
781
+ barometer = copy.deepcopy(tables['barometer'])
782
+ tables['return_barometer'] = barometer.loc[barometer['largest_change'] > 5]
783
+
784
+ return tables
785
+