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
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)
Sign up to request clarification or add additional context in comments.

1 Comment

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

Your Answer

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.