@@ -68,7 +68,8 @@ def get_indicator_signal(self, indicator: Optional[str]= None) -> Dict:
6868 return self ._indicator_signals
6969
7070
71- def set_indicator_signal (self , indicator : str , buy : float , sell : float , condition_buy : Any , condition_sell : Any ) -> None :
71+ def set_indicator_signal (self , indicator : str , buy : float , sell : float , condition_buy : Any , condition_sell : Any ,
72+ buy_max : float = None , sell_max : float = None , condition_buy_max : Any = None , condition_sell_max : Any = None ) -> None :
7273 """Return the raw Pandas Dataframe Object.
7374
7475 Arguments:
@@ -79,11 +80,23 @@ def set_indicator_signal(self, indicator: str, buy: float, sell: float, conditio
7980
8081 sell {float} -- The sell signal threshold for the indicator.
8182
82- condition_buy {str} -- The operator which is used to evaluate the buy condition. For example, `">"` would
83+ condition_buy {str} -- The operator which is used to evaluate the ` buy` condition. For example, `">"` would
8384 represent greater than or from the `operator` module it would represent `operator.gt`.
8485
85- condition_buy {str} -- The operator which is used to evaluate the sell condition. For example, `">"` would
86+ condition_sell {str} -- The operator which is used to evaluate the ` sell` condition. For example, `">"` would
8687 represent greater than or from the `operator` module it would represent `operator.gt`.
88+
89+ buy_max {float} -- If the buy threshold has a maximum value that needs to be set, then set the `buy_max` threshold.
90+ This means if the signal exceeds this amount it WILL NOT PURCHASE THE INSTRUMENT. (defaults to None).
91+
92+ sell_max {float} -- If the sell threshold has a maximum value that needs to be set, then set the `buy_max` threshold.
93+ This means if the signal exceeds this amount it WILL NOT SELL THE INSTRUMENT. (defaults to None).
94+
95+ condition_buy_max {str} -- The operator which is used to evaluate the `buy_max` condition. For example, `">"` would
96+ represent greater than or from the `operator` module it would represent `operator.gt`. (defaults to None).
97+
98+ condition_sell_max {str} -- The operator which is used to evaluate the `sell_max` condition. For example, `">"` would
99+ represent greater than or from the `operator` module it would represent `operator.gt`. (defaults to None).
87100 """
88101
89102 # Add the key if it doesn't exist.
@@ -96,6 +109,12 @@ def set_indicator_signal(self, indicator: str, buy: float, sell: float, conditio
96109 self ._indicator_signals [indicator ]['buy_operator' ] = condition_buy
97110 self ._indicator_signals [indicator ]['sell_operator' ] = condition_sell
98111
112+ # Add the max signals
113+ self ._indicator_signals [indicator ]['buy_max' ] = buy_max
114+ self ._indicator_signals [indicator ]['sell_max' ] = sell_max
115+ self ._indicator_signals [indicator ]['buy_operator_max' ] = condition_buy_max
116+ self ._indicator_signals [indicator ]['sell_operator_max' ] = condition_sell_max
117+
99118 @property
100119 def price_data_frame (self ) -> pd .DataFrame :
101120 """Return the raw Pandas Dataframe Object.
@@ -562,6 +581,167 @@ def macd(self, fast_period: int = 12, slow_period: int = 26) -> pd.DataFrame:
562581
563582 return self ._frame
564583
584+ def mass_index (self , period : int = 9 ) -> pd .DataFrame :
585+ """Calculates the Mass Index indicator.
586+
587+ Arguments:
588+ ----
589+ period {int} -- The number of periods to use when calculating
590+ the mass index. (default: {9})
591+
592+ Returns:
593+ ----
594+ {pd.DataFrame} -- A Pandas data frame with the Mass Index included.
595+
596+ Usage:
597+ ----
598+ >>> historical_prices_df = trading_robot.grab_historical_prices(
599+ start=start_date,
600+ end=end_date,
601+ bar_size=1,
602+ bar_type='minute'
603+ )
604+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
605+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
606+ >>> indicator_client.mass_index(period=9)
607+ """
608+
609+ locals_data = locals ()
610+ del locals_data ['self' ]
611+
612+ column_name = 'mass_index'
613+ self ._current_indicators [column_name ] = {}
614+ self ._current_indicators [column_name ]['args' ] = locals_data
615+ self ._current_indicators [column_name ]['func' ] = self .mass_index
616+
617+ # Calculate the Diff.
618+ self ._frame ['diff' ] = self ._frame ['high' ] - self ._frame ['low' ]
619+
620+ # Calculate Mass Index 1
621+ self ._frame ['mass_index_1' ] = self ._frame ['diff' ].transform (
622+ lambda x : x .ewm (span = period , min_periods = period - 1 ).mean ()
623+ )
624+
625+ # Calculate Mass Index 2
626+ self ._frame ['mass_index_2' ] = self ._frame ['mass_index_1' ].transform (
627+ lambda x : x .ewm (span = period , min_periods = period - 1 ).mean ()
628+ )
629+
630+ # Grab the raw index.
631+ self ._frame ['mass_index_raw' ] = self ._frame ['mass_index_1' ] / self ._frame ['mass_index_2' ]
632+
633+ # Calculate the Mass Index.
634+ self ._frame ['mass_index' ] = self ._frame ['mass_index_raw' ].transform (
635+ lambda x : x .rolling (window = 25 ).sum ()
636+ )
637+
638+ # Clean up before sending back.
639+ self ._frame .drop (
640+ labels = ['diff' , 'mass_index_1' , 'mass_index_2' , 'mass_index_raw' ],
641+ axis = 1 ,
642+ inplace = True
643+ )
644+
645+ return self ._frame
646+
647+ def kst_oscillator (self , r1 : int , r2 : int , r3 : int , r4 : int , n1 : int , n2 : int , n3 : int , n4 : int ) -> pd .DataFrame :
648+ """Calculates the Mass Index indicator.
649+
650+ Arguments:
651+ ----
652+ period {int} -- The number of periods to use when calculating
653+ the mass index. (default: {9})
654+
655+ Returns:
656+ ----
657+ {pd.DataFrame} -- A Pandas data frame with the Mass Index included.
658+
659+ Usage:
660+ ----
661+ >>> historical_prices_df = trading_robot.grab_historical_prices(
662+ start=start_date,
663+ end=end_date,
664+ bar_size=1,
665+ bar_type='minute'
666+ )
667+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
668+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
669+ >>> indicator_client.mass_index(period=9)
670+ """
671+
672+ locals_data = locals ()
673+ del locals_data ['self' ]
674+
675+ column_name = 'kst_oscillator'
676+ self ._current_indicators [column_name ] = {}
677+ self ._current_indicators [column_name ]['args' ] = locals_data
678+ self ._current_indicators [column_name ]['func' ] = self .kst_oscillator
679+
680+ # Calculate the ROC 1.
681+ self ._frame ['roc_1' ] = self ._frame ['close' ].diff (r1 - 1 ) / self ._frame ['close' ].shift (r1 - 1 )
682+
683+ # Calculate the ROC 2.
684+ self ._frame ['roc_2' ] = self ._frame ['close' ].diff (r2 - 1 ) / self ._frame ['close' ].shift (r2 - 1 )
685+
686+ # Calculate the ROC 3.
687+ self ._frame ['roc_3' ] = self ._frame ['close' ].diff (r3 - 1 ) / self ._frame ['close' ].shift (r3 - 1 )
688+
689+ # Calculate the ROC 4.
690+ self ._frame ['roc_4' ] = self ._frame ['close' ].diff (r4 - 1 ) / self ._frame ['close' ].shift (r4 - 1 )
691+
692+
693+ # Calculate the Mass Index.
694+ self ._frame ['roc_1_n' ] = self ._frame ['roc_1' ].transform (
695+ lambda x : x .rolling (window = n1 ).sum ()
696+ )
697+
698+ # Calculate the Mass Index.
699+ self ._frame ['roc_2_n' ] = self ._frame ['roc_2' ].transform (
700+ lambda x : x .rolling (window = n2 ).sum ()
701+ )
702+
703+ # Calculate the Mass Index.
704+ self ._frame ['roc_3_n' ] = self ._frame ['roc_3' ].transform (
705+ lambda x : x .rolling (window = n3 ).sum ()
706+ )
707+
708+ # Calculate the Mass Index.
709+ self ._frame ['roc_4_n' ] = self ._frame ['roc_4' ].transform (
710+ lambda x : x .rolling (window = n4 ).sum ()
711+ )
712+
713+ self ._frame [column_name ] = 100 * (self ._frame ['roc_1_n' ] + 2 * self ._frame ['roc_2_n' ] + 3 * self ._frame ['roc_3_n' ] + 4 * self ._frame ['roc_4_n' ])
714+ self ._frame [column_name + "_signal" ] = self ._frame ['column_name' ].transform (
715+ lambda x : x .rolling ().mean ()
716+ )
717+
718+ # Clean up before sending back.
719+ self ._frame .drop (
720+ labels = ['roc_1' , 'roc_2' , 'roc_3' , 'roc_4' , 'roc_1_n' , 'roc_2_n' , 'roc_3_n' , 'roc_4_n' ],
721+ axis = 1 ,
722+ inplace = True
723+ )
724+
725+ return self ._frame
726+
727+ # #KST Oscillator
728+ # def KST(df, r1, r2, r3, r4, n1, n2, n3, n4):
729+ # M = df['Close'].diff(r1 - 1)
730+ # N = df['Close'].shift(r1 - 1)
731+ # ROC1 = M / N
732+ # M = df['Close'].diff(r2 - 1)
733+ # N = df['Close'].shift(r2 - 1)
734+ # ROC2 = M / N
735+ # M = df['Close'].diff(r3 - 1)
736+ # N = df['Close'].shift(r3 - 1)
737+ # ROC3 = M / N
738+ # M = df['Close'].diff(r4 - 1)
739+ # N = df['Close'].shift(r4 - 1)
740+ # ROC4 = M / N
741+ # KST = pd.Series(pd.rolling_sum(ROC1, n1) + pd.rolling_sum(ROC2, n2) * 2 + pd.rolling_sum(ROC3, n3) * 3 + pd.rolling_sum(ROC4, n4) * 4, name = 'KST_' + str(r1) + '_' + str(r2) + '_' + str(r3) + '_' + str(r4) + '_' + str(n1) + '_' + str(n2) + '_' + str(n3) + '_' + str(n4))
742+ # df = df.join(KST)
743+ # return df
744+
565745 def refresh (self ):
566746 """Updates the Indicator columns after adding the new rows."""
567747
0 commit comments