0

I'm looking to improve my OOP skillset and have wrote a script to pull stock data and run some simple statistics. I am able to run and call each function within the Evaluation class individually (commented below), but run into issues when trying to loop through a list of tickers and append the statistics to the initial dataframe.

import datetime as d
import pandas as pd
import pandas_datareader.data as web
import numpy as np
start = d.datetime(2019, 1, 1)
end = d.datetime(2020, 4, 17)
class Security(object):
 def _init__(self, ticker, data_platform, start_date, end_date):
 self.ticker = ticker
 self.data_platform = data_platform
 self.start_date = start_date
 self.end_date = end_date
 def fetch_stock_data(self, ticker, data_platform, start_date, end_date):
 df = web.DataReader(ticker, data_platform, start_date, end_date)
 return df
class Evaluation(Security):
 def __init__(self, ticker, data_platform, start_date, end_date):
 self.df = Security.fetch_stock_data(
 self, ticker, data_platform, start_date, end_date)
 def simple_moving_average(self, period):
 df = self.df
 df['SMA-{}'.format(period)] = df['Adj Close'].rolling(period).mean()
 return df['SMA-{}'.format(period)]
 def exp_moving_average(self, period):
 df = self.df
 df['EMA_{}'.format(period)] = df['Adj Close'].ewm(span=period).mean()
 return df['EMA_{}'.format(period)]
 def rsi(self, period):
 df = self.df
 delta = df['Adj Close'].diff()
 up = delta * 0
 down = up.copy()
 up[delta > 0] = delta[delta > 0]
 down[delta < 0] = -delta[delta < 0]
 up[up.index[period - 1]] = np.mean(up[:period])
 up = up.drop(up.index[:(period - 1)])
 down[down.index[period - 1]] = np.mean(down[:period])
 down = down.drop(down.index[:(period - 1)])
 rs = up.ewm(span=period - 1).mean() / down.ewm(span=period - 1).mean()
 rsi_calc = 100 - 100 / (1 + rs)
 df['rsi'] = rsi_calc
 return df['rsi']
# pypl = Evaluation('PYPL', 'yahoo', start, end)
# print(csgs.df)
# print(csgs.simple_moving_average(50))
# print(csgs.exp_moving_average(26))
# print(csgs.rsi(14))
tickers = ['PYPL', 'TSLA']
for i in tickers:
 df = Evaluation(i, 'yahoo', start, end)
 df['SMA'] = df.simple_moving_average(50)
 df['EMA'] = df.exp_moving_average(26)
 df['rsi'] = df.rsi(14)
 print(df)

I'm receiving a TypeError, which I believe to be related to referencing the Evaluation class.

TypeError: 'Evaluation' object does not support item assignment
asked Apr 18, 2020 at 15:00
2
  • Evaluation returns an object that stores a df attribute not a df Commented Apr 18, 2020 at 15:04
  • So you should do eval = Evaluation(i, 'yahoo', start, end) then eval.df['foo'] = data Commented Apr 18, 2020 at 15:06

1 Answer 1

2

You are confusing object methods with dataframe methods. In your example, df is an Evaluation object, not a dataframe.

>>>e = Evaluation() 
>>>type(e) 
__main__.Evaluation
>>>type(e.df) 
pandas.core.frame.DataFrame

The line df['SMA'] = df.simple_moving_average(50) fails because you can't add a column to an object. You need to use df.df['SMA'] = df.simple_moving_average(50).

As NomadMonad pointed out, it is confusing to use df as the variable name for an Evaluation object, so it would be better to give it a different name. However, eval is a built-in function in python, so it would be better to use e.

Also, you should change the class designs for several reasons

  • In python 3 there is no need to inherit from Object

  • The __init__ method of Security has only one leading underscore instead of two.

  • You don't want Evaluation to inherit from Security. Instead, pass a Security object in the __init__ method of Evaluation.

  • You don't want to call a method that scrapes a website when you instantiate an object. The call to pandas_datareader should be a separate method.

  • You don't need to pass parameters to methods if those values are set in the __init__ method. You can access them with self.

  • You don't need modify the underlying dataframe in your Evaluation methods. Instead return the value produced by the method.

import datetime
import pandas as pd
import numpy as np
import pandas_datareader.data as web 
class Security:
 def _init__(self, ticker, data_platform, start_date, end_date):
 self.ticker = ticker
 self.data_platform = data_platform
 self.start_date = start_date
 self.end_date = end_date
 self.df = None
 def fetch_stock_data(self):
 self.df = web.DataReader(self.ticker, self.data_platform, self.start_date, self.end_date)
class Evaluation:
 def __init__(self, security):
 self.security = security
 def simple_moving_average(self, period):
 df = self.security.df
 return df['Adj Close'].rolling(period).mean()
 def exp_moving_average(self, period):
 df = self.security.df
 return df['Adj Close'].ewm(span=period).mean()
 def rsi(self, period):
 df = self.security.df
 delta = df['Adj Close'].diff()
 up = delta * 0
 down = up.copy()
 up[delta > 0] = delta[delta > 0]
 down[delta < 0] = -delta[delta < 0]
 up[up.index[period - 1]] = np.mean(up[:period])
 up = up.drop(up.index[:(period - 1)])
 down[down.index[period - 1]] = np.mean(down[:period])
 down = down.drop(down.index[:(period - 1)])
 rs = up.ewm(span=period - 1).mean() / down.ewm(span=period - 1).mean()
 return 100 - 100 / (1 + rs)
start = datetime.datetime(2019, 1, 1)
end = datetime.datetime(2019, 4, 17)
s = Security(ticker, 'yahoo', start, end)
e = Evaluation(security=s)
answered Apr 18, 2020 at 16:07

1 Comment

Eric... How can you filter with securities data frame above.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.