0

Let me illustrate with an example.

Say I want to create a class Returns that generates the returns of a stock for example. Returns can be arithmetic or logarithmic, and I want to be able to choose at instantiation which I want. For the moment, I do the following:

def arithmetic(prices):
 daily_ret = []
 for i rows in prices:
 returns.append(prices(i)-prices(i-1))
 return daily_ret
def logarithmic(prices):
 daily_ret = []
 for i rows in prices:
 returns.append(np.ln(prices(i) / prices(i-1))
 return daily_ret
class Returns:
 def __init__(self, stock_ticker, return_calc=arithmetic):
 self.stock_ticker = stock_ticker
 self.return_calc = return_calc
 def calculate_returns(self):
 get_price_data_from_ticker = whatever_data_source()
 if self.return_calc = 'arithmetic':
 returns = arithmetic(get_price_data_from_ticker)
 elif self.return_calc = 'logarithmic':
 returns = logarithmic(get_price_data_from_ticker)
 return returns
x = Returns('AAPL US Equity', return_calc='logarithmic')
print(x.calculate_returns())

This seems like the code is just badly written, and I'm missing something obvious because I've had the same question over and over.

Another thing that I've done is use getattr to dynamically get the name of the function or class I'll use and pass on arguments. Also another thing I've found is using globals() to dynamically create classes.

I'm wondering if there's a macro construction that I'm missing, because even if these 2 solutions above feel quite nice, it feels like there's a deeper structural issue with the code.

Language is Python 3

asked Oct 23, 2018 at 22:12

3 Answers 3

2

In this case, I think using different classes is probably the way to go.

from abc import ABC, abstractmethod
class Returns(ABC):
 def __init__(self, stock_ticker):
 self.stock_ticker = stock_ticker
 def get_price_data_from_ticker(self):
 pass # FIXME: implement getting stuff from the data source here
 @abstractmethod
 def calculate_returns(self, prices):
 pass
 def get_returns(self):
 return self.calculate_returns(self.get_price_data_from_ticker())
class ArithmeticReturns(Returns):
 def calculate_returns(self, prices):
 return [prices(i) - prices(i - 1) for i in prices]
class LogarithmicReturns(Returns):
 def calculate_returns(self, prices):
 return [np.ln(prices(i) / prices(i - 1)) for i in prices]
x = LogarithmicReturns('AAPL US Equity')
print(x.get_returns())

Concepts used:

  • Abstract Base Classes: Returns is now a class that cannot be substantiated, only subclasses that implement all abstract methods can. This means both subclasses implement the same interface without having to do all of the heavy lifting themselves.
  • List comprehensions: l = [a + 1 for a in exp] is equivalent to

    l = []
    for a in exp:
     l.append(a + 1)
    
  • Separation of concerns: calculate_returns is now no longer concerned with how data is collected from the ticker, it purely calculates the returns based on the prices given to it.

Your code contained syntax errors in arithmetic and logarithmic in the loops, so you will need to fix that. If prices is a list, I would use something like [current_price - previous_price for current_price, previous_price in zip(prices[1:], prices)], but I decided to leave it as is in case prices is actually a strange iterable that iterates over the indexes and is indexed by calling.

answered Oct 24, 2018 at 10:28
2
  • 1
    generally speaking, this is Strategy design pattern Commented Nov 20, 2018 at 5:27
  • Isn't a class a little too Java-like for Python? To each language its own, injecting a class instance when all you need is a function seems a little heavy to me! Commented Apr 5, 2019 at 13:43
1

I think you're on the right way; but using some dependency injection might work better in your case. You could do something like this:

def arithmetic(prices):
 daily_ret = []
 for i rows in prices:
 returns.append(prices(i)-prices(i-1))
 return daily_ret
def logarithmic(prices):
 daily_ret = []
 for i rows in prices:
 returns.append(np.ln(prices(i) / prices(i-1))
 return daily_ret
class Returns:
 def __init__(self, stock_ticker, return_calc=arithmetic):
 self.stock_ticker = stock_ticker
 self.return_calc = return_calc
 def calculate_returns(self):
 get_price_data_from_ticker = whatever_data_source()
 return self.return_calc(get_price_data_from_ticker)
x = Returns('AAPL US Equity', return_calc=logarithmic)
print(x.calculate_returns())

by directly passing the function instead of passing a string constant to identify which function to use.

By the way, this is what you were doing in this line:

def __init__(self, stock_ticker, return_calc=arithmetic):

which means that the default value that you are passing right now isn't a string, but directly the function

answered Oct 24, 2018 at 9:30
0

A simpler method is to include both functions as class methods, but assign the visible method in the constructor, like

self.calculate_return = self.logarithmic if .... else self.arithmetic

Then the caller will always use the proper function without doing a test and a deeper method call each time. You can also do this with subclasses but this is simpler, if a little scary for non-python coders.

answered Oct 24, 2018 at 2:39

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.