2

I'm a bit of a noob to classes (mostly done functional programming), so although I've used one specific method to achieve the following, if there a "best practices" that implement what I'm looking for (other than properties & attributes), I'd love to hear about it.

I'm creating a portfolio object that has the following attributes:

  • date_range: a tuple of the first and last date for the relevant period
  • portfolio: the values of the portfolio between date_range[0]:date_range[-1]
  • benchmark: the values of the benchmark between date_range[0]:date_range[-1]

and I'd like to run some statistical calculations on both series (FYI, the Class definition can be found at the bottom of the post).

import pandas
import numpy

#create the date range
In [1]: dt_rng = pandas.DatetimeIndex(start = '01/01/2000', freq = 'b', periods = 100)

#create the portfolio & benchmark series
In  [ 2]: p1 = 1000*numpy.exp(numpy.cumsum(numpy.random.randn(len(dt_rng),)/252.))
In  [ 3]: p2 = 1000*numpy.exp(numpy.cumsum(numpy.random.randn(len(dt_rng),)/252.))
In  [ 4]: port = pandas.Series(p1, dt_rng)
In  [ 5]: bench = pandas.Series(p2, dt_rng)

#create the Portfolio Object (Class Code at the bottom)
In  [ 6]: port_object = PortObj(port, bench)
In  [ 7]: port_object.port_return()
Out [10]: -0.00066236017291987359
In  [11]: port_object.bech_return()
Out [12]: -0.031054475224739697
In  [13]: port_object.alpha()
Out [14]: 0.030392115051819824
In  [15]: port_object.date_range
Out [16]: 
(Timestamp('2000-01-03 00:00:00', tz=None),
 Timestamp('2000-05-19 00:00:00', tz=None))

Now I would like to change the range of the dates that is used in the calculation of portfolio metrics, so let's say I chose an interval of dt_rng[15], dt_rng[60]. So:

In  [14]: port_object.date_range = ((dt_rng[15], dt_rng[50]))
Out [15]:
(Timestamp('2000-01-24 00:00:00', tz=None),
 Timestamp('2000-03-13 00:00:00', tz=None))

So we know the date_range has changed, but my @set methods didn't function properly, as can be seen by (one of many methods):

In  [16:]: port_object.alpha()
Out [17:]: 0.030392115051819824 #same as above

I've tried to follow this post but wasn't able to implement it inside of my class.

Here's the class:

class PortObj:
    def __init__(self, portfolio, benchmark):
        self.date_range = ((portfolio.index[0], portfolio.index[-1]))
        self.portfolio = portfolio
        self.benchmark = benchmark

    @property
    def date_range(self):
        return self.date_range
    @date_range.setter
    def date_range(self, date_range):
        self.date_range = date_range
        self.portfolio = self.portfolio.loc[date_range[0]:date_range[1]]
        self.benchmark = self.benchmark.loc[date_range[0]:date_range[1]]

    @property
    def portfolio(self):
        return self.portfolio
    @portfolio.setter
    def portfolio(self, date_range):
        self.date_range = date_range
        self.portfolio = self.portfolio.loc[date_range[0]:date_range[1]]
        self.benchmark = self.benchmark.loc[date_range[0]:date_range[1]]

    @property
    def benchmark(self):
        return self.benchmark
    @portfolio.setter
    def benchmark(self, date_range):
        self.date_range = date_range
        self.portfolio = self.portfolio.loc[date_range[0]:date_range[1]]
        self.benchmark = self.benchmark.loc[date_range[0]:date_range[1]]

    def port_return(self):
        return numpy.divide(self.portfolio[-1], self.portfolio[0]) - 1

    def bench_return(self):
        return numpy.divide(self.benchmark[-1], self.benchmark[0]) - 1

    def alpha(self):
        return self.port_return() - self.bench_return()

What would folks recommend as the most effective way to "update portfolio Series and benchmark Series" when the date_range is changed?

Thanks so much,

-B

Community
  • 1
  • 1
benjaminmgross
  • 2,052
  • 1
  • 24
  • 29

2 Answers2

4

Take a look at the property docs:

http://docs.python.org/2/library/functions.html#property

Specifically the example:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

You need to store your value in a separate "private" variable, instead of using the same name of the property inside the getter/setter. Normally, this would give you a recursion error, but since you're not inheriting from object, you're using an old-style class, which doesn't work properly with descriptors. Because of this, your constructor's assignment of self.date_range = ... replaces your property anyway.

In short, you need to:

  • inherit from (object) for properties to work right.
  • use a variable with a different name than your property (like self._date_range)
bj0
  • 7,893
  • 5
  • 38
  • 49
0

There are a few things which smell a little funny to me here ...

  1. If you're on python2.x, you should inherit from object since properties are only documented to work with new style classes...
  2. You shouldn't have "attributes" with the same name as one of your properities...e.g.
    @property
    def portfolio(self):
        return self.portfolio

I'm actually surprised something like that doesn't give you a recursion error... Generally, for stuff like this, you wrap a different (non-property) attribute:

@property
def portfolio(self):
    return self._portfolio  # Note the leading underscore.

@portfolio.setter
def portfolio(self, date_range):
    self._date_range = date_range
    self._portfolio = self.portfolio.loc[date_range[0]:date_range[1]]
    self._benchmark = self.benchmark.loc[date_range[0]:date_range[1]]
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • so in the constructor, are you assigning: `self.date_range = ((portfolio.index[0], portfolio.index[-1]))` or are you adding the underscore there as well? i.e. `self._date_range = ((portfolio.index[0], portfolio.index[-1]))` – benjaminmgross Dec 27 '13 at 19:21
  • @Factor3 -- Probably using the underscore. It depends though. You use the underscore when you want to avoid the magic of the property and you don't use the underscore when you want the property magic to take effect. – mgilson Dec 27 '13 at 19:45