Question

So I have written my first Python application of more than trivial size, and I did it procedurally because that was the information I had best absorbed to date. I now want to refactor it to try and (finally) understand classes and OOP. My question is about the best practices for designing classes as one develops from a simple beginning.

So let's say I have a application that simulates an investment portfolio of short equity put options. This means at any given time I could have a portfolio (container) composed of holdings of short put options, as well as long stocks (from short options that were later exercised/assigned).

So in terms of nouns there is a lot going on. There are data/attributes/methods applicable to any any equity option, data/attributes/methods that are specific to put options (vs call options), and data/attributes/methods that are specific to short put options (vs long put options). I could conceptually have:

class Option(object):
    """Describes an Option"""
    pass

class Put(option):
    """Describes an Option that is a Put"""
    pass

class Call(option):
    """Describes an Option that is a Call"""
    pass

class ShortPut(Put):
    """Describes a Put that is written (sold short)"""
    pass

class LongPut(Put):
    """Describes a Put that is purchased"""
    pass

class ShortCall(Call):
    """Describes a Put that is written (sold short)"""
    pass

class LongCall(Call):
    """Describes a Put that is purchased"""
    pass

# then more classes to describe stocks, holdings of a 
# given quantity of a stock or option at a specific price 
# on a specific date, and a portfolio to contain all the 
# holdings etc etc 

I am well on my way to a complete taxonomy. Maybe there is too much inheritance, which I have read can be a bad thing. But it does describe an object and allows for differing behavior/data as additional/different attributes are added. However, it feels like I have a huge amount of scaffolding that does nothing because I can describe everything I need at this stage with just a ShortPut object.

Also, I have been focused very much on learning good unit testing, keeping things simple, and building as you go. So in reality I think it should look like this:

class ShortPut(object):
    """Describes a short put"""
    pass

That is the only kind of option I have in my simulation at the moment, so I think of this as the "interface." It seems to me that if I encapsulate this object correctly, then I can start with just this one object and wait to refactor into some larger taxonomy only as warranted. For example, if I later need a long put, then I would be repeating a put description inside this new object and that would violate DRY. So instead I would create a higher-level (base?) class to hold the common code, and I would describe the differences in behavior with separate inherited classes:

class Put(object):
    """Describes an Option that is a Put"""
    pass

class ShortPut(Put):
    """Describes a Put that is written (sold short)"""
    pass

class LongPut(Put):
    """Describes a Put that is purchased"""
    pass

And then I can just continue with that recursive process as my descriptions and use cases become more complex. But, if I encapsulate correctly, my goal should be to never need to adjust any of the "downstream" code - when I want a ShortPut I should be able to keep making the same request regardless of how complicated and distributed it gets under the hood. Finally, everything in my code actually does something related to the intended outcome, and so unit tests are meaningful to the outcome.

Am I thinking about this problem correctly? It seems obvious to me, but then I have had to rip up a lot of what I written in the past because what I assumed to be "obvious" turned out to be dead wrong. I apologize if this seems too basic or if I don't have all the terms down exactly - I am still learning. Everything I have been reading has focused on short examples of how to design classes and illustrate concepts like inheritance or overloading, but I haven't come across as much about how to design simply with an eye towards future complexity or extensibility. Thanks!

Was it helpful?

Solution

Am I thinking about this problem correctly?

Pretty much. Especially if you’re new to OOP as you say, you shouldn’t be forcing structures onto your code on the grounds of conceptual purity or completeness. “Good unit testing, keeping things simple, and building as you go” is generally a good way to go about writing a (Python) program. But don’t expect to never have to change interfaces and chase broken usages throughout your codebase—it’s not just “building” but also sometimes “breaking and rebuilding as you go”.

OTHER TIPS

Because of your self-confessed scaffolding tendency, my advice to you at this point would be, write the actual functionality with a fairly monolithic design, create test cases early, and then go back to refactor to smaller, prettier, and more modular only when you see a distinct opportunity -- repeated code, similar cases, unattractive compromises, bad smells. Fix them when you have a reasonably specific idea about how you should redo them (and generously sprinkle your code with honest TODO or FIXME comments when you don't. Learn to recognize when you are about to try to fool yourself.)

I know it's common practice to stub with pass cases but if you can avoid doing that and focus on one thing at a time, maybe you'll get to feel more productive. Me, I'm always scared that I will leave in a silly pass because I forgot.

I think the soundness of a design depends very much on the scope. There is so much we can talk about, so I will just share a few thought that springs to my mind right now..

From what I see here, your primary concern seems to be pricing, so maybe this OO model is adequate (You do not worry about settlement, P/L and cashflow for example). Otherwise it is too simple.

Long/Short a Put/Call is inherently a trading strategy. It is probably not right to be represented as a kind of option. Along the same line, I would expect to see option classes like EuropeanCall/Put or AmericanCall/Put.

That is the only kind of option I have in my simulation at the moment, so I think of this as the "interface." It seems to me that if I encapsulate this object correctly, then I can start with just this one object and wait to refactor into some larger taxonomy only as warranted. For example, if I later need a long put, then I would be repeating a put description inside this new object and that would violate DRY. So instead I would create a higher-level (base?) class to hold the common code, and I would describe the differences in behavior with separate inherited classes:

Mixin can be an answer here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top