1
+ import numpy as np
2
+ import pandas as pd
3
+
1
4
from typing import Tuple
2
5
from typing import List
3
6
from typing import Iterable
4
7
5
- class Indicator ():
6
- pass
8
+ from pyrobot .stock_frame import StockFrame
9
+
10
+
11
+ class Indicators ():
12
+
13
+
14
+ def __init__ (self , price_data_frame : StockFrame ) -> None :
15
+ """Initalizes the Indicator Client.
16
+
17
+ Arguments:
18
+ ----
19
+ price_data_frame {pyrobot.StockFrame} -- The price data frame which is used to add indicators to.
20
+ At a minimum this data frame must have the following columns: `['timestamp','close','open','high','low']`.
21
+
22
+ Usage:
23
+ ----
24
+ >>> historical_prices_df = trading_robot.grab_historical_prices(
25
+ start=start_date,
26
+ end=end_date,
27
+ bar_size=1,
28
+ bar_type='minute'
29
+ )
30
+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
31
+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
32
+ >>> price_data_frame_indicators = indicator_client.price_data_frame
33
+ """
34
+
35
+ self ._price_data_frame : StockFrame = price_data_frame
36
+ self ._price_groups = self ._price_data_frame .symbol_groups
37
+
38
+ if self .is_multi_index :
39
+ True
40
+
41
+ @property
42
+ def price_data_frame (self ) -> pd .DataFrame :
43
+ return self ._price_data_frame .frame
44
+
45
+ @price_data_frame .setter
46
+ def price_data_frame (self , price_data_frame ):
47
+ self ._price_data_frame = price_data_frame
48
+
49
+ @price_data_frame .deleter
50
+ def price_data_frame (self ):
51
+ del self ._price_data_frame
52
+
53
+ @property
54
+ def is_multi_index (self ):
55
+ if isinstance (self ._price_data_frame .frame .index , pd .MultiIndex ):
56
+ return True
57
+ else :
58
+ return False
59
+
60
+ def change_in_price (self ) -> pd .DataFrame :
61
+ """Calculates the Change in Price.
62
+
63
+ Returns:
64
+ ----
65
+ pd.DataFrame -- A data frame with the Change in Price.
66
+ """
67
+
68
+ self ._price_data_frame .frame ['change_in_price' ] = self ._price_data_frame .frame .groupby (
69
+ by = 'symbol' ,
70
+ as_index = False
71
+ )['close' ].transform (
72
+ lambda x : x .diff ()
73
+ )
74
+
75
+ def rsi (self , period : int , method : str = 'wilders' ) -> pd .DataFrame :
76
+ """Calculates the Relative Strength Index (RSI).
77
+
78
+ Arguments:
79
+ ----
80
+ period {int} -- The number of periods to use to calculate the RSI.
81
+
82
+ Keyword Arguments:
83
+ ----
84
+ method {str} -- The calculation methodology. (default: {'wilders'})
85
+
86
+ Returns:
87
+ ----
88
+ pd.DataFrame -- A Pandas data frame with the RSI indicator included.
89
+
90
+ Usage:
91
+ ----
92
+ >>> historical_prices_df = trading_robot.grab_historical_prices(
93
+ start=start_date,
94
+ end=end_date,
95
+ bar_size=1,
96
+ bar_type='minute'
97
+ )
98
+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
99
+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
100
+ >>> indicator_client.rsi(period=14)
101
+ >>> price_data_frame = inidcator_client.price_data_frame
102
+ """
103
+
104
+ # Define the price data frame.
105
+ price_frame = self ._price_data_frame .frame
106
+
107
+ # First calculate the Change in Price.
108
+ if 'change_in_price' not in price_frame .columns :
109
+ self .change_in_price ()
110
+
111
+ # Define the up days.
112
+ price_frame ['up_day' ] = price_frame .groupby (
113
+ by = 'symbol' ,
114
+ as_index = False
115
+ )['change_in_price' ].transform (lambda x : np .where (x >= 0 , x , 0 ))
116
+
117
+ # Define the down days.
118
+ price_frame ['down_day' ] = price_frame .groupby (
119
+ by = 'symbol' ,
120
+ as_index = False
121
+ )['change_in_price' ].transform (lambda x : np .where (x < 0 , x .abs (), 0 ))
122
+
123
+ # Calculate the EWMA (Exponential Weighted Moving Average), meaning older values are given less weight compared to newer values.
124
+ price_frame ['ewma_up' ] = price_frame .groupby ('symbol' )['up_day' ].transform (lambda x : x .ewm (span = period ).mean ())
125
+ price_frame ['ewma_down' ] = price_frame .groupby ('symbol' )['down_day' ].transform (lambda x : x .ewm (span = period ).mean ())
126
+
127
+ # Calculate the Relative Strength
128
+ relative_strength = price_frame ['ewma_up' ] / price_frame ['ewma_down' ]
129
+
130
+ # Calculate the Relative Strength Index
131
+ relative_strength_index = 100.0 - (100.0 / (1.0 + relative_strength ))
132
+
133
+ # Add the info to the data frame.
134
+ price_frame ['rsi' ] = np .where (relative_strength_index == 0 , 100 , 100 - (100 / (1 + relative_strength_index )))
135
+
136
+ # Clean up before sending back.
137
+ price_frame .drop (labels = ['ewma_up' , 'ewma_down' , 'down_day' , 'up_day' , 'change_in_price' ], axis = 1 , inplace = True )
138
+
139
+ # Reassign?
140
+ # self.price_data_frame(price_frame)
141
+
142
+ return price_frame
143
+
144
+ def sma (self , period : int ) -> pd .DataFrame :
145
+ """Calculates the Simple Moving Average (SMA).
146
+
147
+ Arguments:
148
+ ----
149
+ period {int} -- The number of periods to use when calculating the SMA.
150
+
151
+ Returns:
152
+ ----
153
+ pd.DataFrame -- A Pandas data frame with the SMA indicator included.
154
+
155
+ Usage:
156
+ ----
157
+ >>> historical_prices_df = trading_robot.grab_historical_prices(
158
+ start=start_date,
159
+ end=end_date,
160
+ bar_size=1,
161
+ bar_type='minute'
162
+ )
163
+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
164
+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
165
+ >>> indicator_client.sma(period=100)
166
+ >>> price_data_frame = inidcator_client.price_data_frame
167
+ """
168
+
169
+ # Grab the Price Frame.
170
+ price_frame = self ._price_data_frame .frame
171
+
172
+ # Add the SMA
173
+ price_frame ['sma' ] = price_frame .groupby ('symbol' )['close' ].transform (lambda x : x .rolling (window = period ).mean ())
174
+
175
+ return price_frame
176
+
177
+ def ema (self , period : int , alpha : float = 0.0 ) -> pd .DataFrame :
178
+ """Calculates the Exponential Moving Average (EMA).
179
+
180
+ Arguments:
181
+ ----
182
+ period {int} -- The number of periods to use when calculating the EMA.
183
+
184
+ Returns:
185
+ ----
186
+ pd.DataFrame -- A Pandas data frame with the EMA indicator included.
187
+
188
+ Usage:
189
+ ----
190
+ >>> historical_prices_df = trading_robot.grab_historical_prices(
191
+ start=start_date,
192
+ end=end_date,
193
+ bar_size=1,
194
+ bar_type='minute'
195
+ )
196
+ >>> price_data_frame = pd.DataFrame(data=historical_prices)
197
+ >>> indicator_client = Indicators(price_data_frame=price_data_frame)
198
+ >>> indicator_client.ema(period=50)
199
+ >>> price_data_frame = inidcator_client.price_data_frame
200
+ """
201
+
202
+ # Grab the Price Frame.
203
+ price_frame = self ._price_data_frame .frame
204
+
205
+ # Add the EMA
206
+ price_frame ['ema' ] = price_frame .groupby ('symbol' , sort = True )['close' ].transform (
207
+ lambda x : x .ewm (span = period ).mean ()
208
+ )
209
+
210
+ return price_frame
0 commit comments