@@ -643,6 +643,256 @@ def mass_index(self, period: int = 9) -> pd.DataFrame:
643643 )
644644
645645 return self ._frame
646+
647+ def force_index (self , period : int ) -> pd .DataFrame :
648+ """Calculates the Force Index.
649+
650+ Arguments:
651+ ----
652+ period {int} -- The number of periods to use when calculating
653+ the force index.
654+
655+ Returns:
656+ ----
657+ {pd.DataFrame} -- A Pandas data frame with the force 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.force_index(period=9)
670+ """
671+
672+ locals_data = locals ()
673+ del locals_data ['self' ]
674+
675+ column_name = 'force_index'
676+ self ._current_indicators [column_name ] = {}
677+ self ._current_indicators [column_name ]['args' ] = locals_data
678+ self ._current_indicators [column_name ]['func' ] = self .force_index
679+
680+ # Calculate the Force Index.
681+ self ._frame [column_name ] = self ._frame ['close' ].diff (period ) * self ._frame ['volume' ].diff (period )
682+
683+ return self ._frame
684+
685+ def ease_of_movement (self , period : int ) -> pd .DataFrame :
686+ """Calculates the Ease of Movement.
687+
688+ Arguments:
689+ ----
690+ period {int} -- The number of periods to use when calculating
691+ the Ease of Movement.
692+
693+ Returns:
694+ ----
695+ {pd.DataFrame} -- A Pandas data frame with the Ease of Movement included.
696+
697+ Usage:
698+ ----
699+ >>> historical_prices_df = trading_robot.grab_historical_prices(
700+ start=start_date,
701+ end=end_date,
702+ bar_size=1,
703+ bar_type='minute'
704+ )
705+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
706+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
707+ >>> indicator_client.ease_of_movement(period=9)
708+ """
709+
710+ locals_data = locals ()
711+ del locals_data ['self' ]
712+
713+ column_name = 'ease_of_movement'
714+ self ._current_indicators [column_name ] = {}
715+ self ._current_indicators [column_name ]['args' ] = locals_data
716+ self ._current_indicators [column_name ]['func' ] = self .ease_of_movement
717+
718+ # Calculate the ease of movement.
719+ high_plus_low = (self ._frame ['high' ].diff (1 ) + self ._frame ['low' ].diff (1 ))
720+ diff_divi_vol = (self ._frame ['high' ] - self ._frame ['low' ]) / (2 * self ._frame ['volume' ])
721+ self ._frame ['ease_of_movement_raw' ] = high_plus_low * diff_divi_vol
722+
723+ # Calculate the Rolling Average of the Ease of Movement.
724+ self ._frame ['ease_of_movement' ] = self ._frame ['ease_of_movement_raw' ].transform (
725+ lambda x : x .rolling (window = period ).mean ()
726+ )
727+
728+ # Clean up before sending back.
729+ self ._frame .drop (
730+ labels = ['ease_of_movement_raw' ],
731+ axis = 1 ,
732+ inplace = True
733+ )
734+
735+ return self ._frame
736+
737+ def commodity_channel_index (self , period : int ) -> pd .DataFrame :
738+ """Calculates the Commodity Channel Index.
739+
740+ Arguments:
741+ ----
742+ period {int} -- The number of periods to use when calculating
743+ the Commodity Channel Index.
744+
745+ Returns:
746+ ----
747+ {pd.DataFrame} -- A Pandas data frame with the Commodity Channel Index included.
748+
749+ Usage:
750+ ----
751+ >>> historical_prices_df = trading_robot.grab_historical_prices(
752+ start=start_date,
753+ end=end_date,
754+ bar_size=1,
755+ bar_type='minute'
756+ )
757+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
758+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
759+ >>> indicator_client.commodity_channel_index(period=9)
760+ """
761+
762+ locals_data = locals ()
763+ del locals_data ['self' ]
764+
765+ column_name = 'commodity_channel_index'
766+ self ._current_indicators [column_name ] = {}
767+ self ._current_indicators [column_name ]['args' ] = locals_data
768+ self ._current_indicators [column_name ]['func' ] = self .commodity_channel_index
769+
770+ # Calculate the Typical Price.
771+ self ._frame ['typical_price' ] = (self ._frame ['high' ] + self ._frame ['low' ] + self ._frame ['close' ]) / 3
772+
773+ # Calculate the Rolling Average of the Typical Price.
774+ self ._frame ['typical_price_mean' ] = self ._frame ['pp' ].transform (
775+ lambda x : x .rolling (window = period ).mean ()
776+ )
777+
778+ # Calculate the Rolling Standard Deviation of the Typical Price.
779+ self ._frame ['typical_price_std' ] = self ._frame ['pp' ].transform (
780+ lambda x : x .rolling (window = period ).std ()
781+ )
782+
783+ # Calculate the Commodity Channel Index.
784+ self ._frame [column_name ] = self ._frame ['typical_price_mean' ] / self ._frame ['typical_price_std' ]
785+
786+ # Clean up before sending back.
787+ self ._frame .drop (
788+ labels = ['typical_price' , 'typical_price_mean' , 'typical_price_std' ],
789+ axis = 1 ,
790+ inplace = True
791+ )
792+
793+ return self ._frame
794+
795+ def standard_deviation (self , period : int ) -> pd .DataFrame :
796+ """Calculates the Standard Deviation.
797+
798+ Arguments:
799+ ----
800+ period {int} -- The number of periods to use when calculating
801+ the standard deviation.
802+
803+ Returns:
804+ ----
805+ {pd.DataFrame} -- A Pandas data frame with the Standard Deviation included.
806+
807+ Usage:
808+ ----
809+ >>> historical_prices_df = trading_robot.grab_historical_prices(
810+ start=start_date,
811+ end=end_date,
812+ bar_size=1,
813+ bar_type='minute'
814+ )
815+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
816+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
817+ >>> indicator_client.standard_deviation(period=9)
818+ """
819+
820+ locals_data = locals ()
821+ del locals_data ['self' ]
822+
823+ column_name = 'standard_deviation'
824+ self ._current_indicators [column_name ] = {}
825+ self ._current_indicators [column_name ]['args' ] = locals_data
826+ self ._current_indicators [column_name ]['func' ] = self .standard_deviation
827+
828+ # Calculate the Standard Deviation.
829+ self ._frame [column_name ] = self ._frame ['close' ].transform (
830+ lambda x : x .ewm (span = period ).std ()
831+ )
832+
833+ return self ._frame
834+
835+ def chaikin_oscillator (self , period : int ) -> pd .DataFrame :
836+ """Calculates the Chaikin Oscillator.
837+
838+ Arguments:
839+ ----
840+ period {int} -- The number of periods to use when calculating
841+ the Chaikin Oscillator.
842+
843+ Returns:
844+ ----
845+ {pd.DataFrame} -- A Pandas data frame with the Chaikin Oscillator included.
846+
847+ Usage:
848+ ----
849+ >>> historical_prices_df = trading_robot.grab_historical_prices(
850+ start=start_date,
851+ end=end_date,
852+ bar_size=1,
853+ bar_type='minute'
854+ )
855+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
856+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
857+ >>> indicator_client.chaikin_oscillator(period=9)
858+ """
859+
860+ locals_data = locals ()
861+ del locals_data ['self' ]
862+
863+ column_name = 'chaikin_oscillator'
864+ self ._current_indicators [column_name ] = {}
865+ self ._current_indicators [column_name ]['args' ] = locals_data
866+ self ._current_indicators [column_name ]['func' ] = self .chaikin_oscillator
867+
868+ # Calculate the Money Flow Multiplier.
869+ money_flow_multiplier_top = 2 * (self ._frame ['close' ] - self ._frame ['high' ] - self ._frame ['low' ])
870+ money_flow_multiplier_bot = (self ._frame ['high' ] - self ._frame ['low' ])
871+
872+ # Calculate Money Flow Volume
873+ self ._frame ['money_flow_volume' ] = (money_flow_multiplier_top / money_flow_multiplier_bot ) * self ._frame ['volume' ]
874+
875+ # Calculate the 3-Day moving average of the Money Flow Volume.
876+ self ._frame ['money_flow_volume_3' ] = self ._frame ['money_flow_volume' ].transform (
877+ lambda x : x .ewm (span = 3 , min_periods = 2 ).mean ()
878+ )
879+
880+ # Calculate the 10-Day moving average of the Money Flow Volume.
881+ self ._frame ['money_flow_volume_10' ] = self ._frame ['money_flow_volume' ].transform (
882+ lambda x : x .ewm (span = 10 , min_periods = 9 ).mean ()
883+ )
884+
885+ # Calculate the Chaikin Oscillator.
886+ self ._frame [column_name ] = self ._frame ['money_flow_volume_3' ] - self ._frame ['money_flow_volume_10' ]
887+
888+ # Clean up before sending back.
889+ self ._frame .drop (
890+ labels = ['money_flow_volume_3' , 'money_flow_volume_10' , 'money_flow_volume' ],
891+ axis = 1 ,
892+ inplace = True
893+ )
894+
895+ return self ._frame
646896
647897 def kst_oscillator (self , r1 : int , r2 : int , r3 : int , r4 : int , n1 : int , n2 : int , n3 : int , n4 : int ) -> pd .DataFrame :
648898 """Calculates the Mass Index indicator.
@@ -714,7 +964,7 @@ def kst_oscillator(self, r1: int, r2: int, r3: int, r4: int, n1: int, n2: int, n
714964 self ._frame [column_name + "_signal" ] = self ._frame ['column_name' ].transform (
715965 lambda x : x .rolling ().mean ()
716966 )
717-
967+
718968 # Clean up before sending back.
719969 self ._frame .drop (
720970 labels = ['roc_1' , 'roc_2' , 'roc_3' , 'roc_4' , 'roc_1_n' , 'roc_2_n' , 'roc_3_n' , 'roc_4_n' ],
@@ -724,6 +974,7 @@ def kst_oscillator(self, r1: int, r2: int, r3: int, r4: int, n1: int, n2: int, n
724974
725975 return self ._frame
726976
977+
727978# #KST Oscillator
728979# def KST(df, r1, r2, r3, r4, n1, n2, n3, n4):
729980# M = df['Close'].diff(r1 - 1)
0 commit comments