Skip to content

Commit 11b18a2

Browse files
committed
add new indicators
1 parent 5d88da3 commit 11b18a2

3 files changed

Lines changed: 273 additions & 2 deletions

File tree

pyrobot/indicators.py

Lines changed: 252 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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)

pyrobot/robot.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pprint
44
import pathlib
55
import pandas as pd
6+
import pkg_resources
67

78
from datetime import time
89
from datetime import datetime
@@ -18,8 +19,15 @@
1819
from pyrobot.portfolio import Portfolio
1920
from pyrobot.stock_frame import StockFrame
2021

22+
current_td_version = pkg_resources.get_distribution('td-ameritrade-python-api').version
23+
2124
from td.client import TDClient
22-
from td.utils import milliseconds_since_epoch
25+
26+
if current_td_version == '0.3.0':
27+
from td.utils import TDUtilities
28+
milliseconds_since_epoch = TDUtilities().milliseconds_since_epoch
29+
else:
30+
from td.utils import milliseconds_since_epoch
2331

2432

2533
class PyRobot():

trading_robot_indicators.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@
8787
# Add the Mass Index.
8888
indicator_client.mass_index(period=9)
8989

90+
# Add the K-Oscillator
91+
indicator_client.kst_oscillator(
92+
r1=1,
93+
r2=2,
94+
r3=3,
95+
r4=4,
96+
n1=1,
97+
n2=2,
98+
n3=3,
99+
n4=4
100+
)
101+
90102
while True:
91103

92104
# Grab the latest bar.

0 commit comments

Comments
 (0)