Search code examples
pythonfunctionlocal-variables

Best practice to calculate the Delta of an option


An option is a derivative contract based on the value of some underlying security (usually a stock's price). Formally, under a pricing model, the option price is a function of s0, the current underlying security price, a lot of other args (volatility, interest rate, dividend rate etc.), and some possible kwargs.

The Delta of an option is the rate of change of its price with regard to the underlying security's price. In practice, this is implemented like

(calc_price(s0 + ds, **kwargs) - calc_price(s0 - ds, **kwargs)) / (2 * ds)

What I want to do is implement get_delta as a method of the Option class So now I have implemented a pricing model for the option as:

class Option:
    def __init__(self, s0, lots_of_args, **kwargs):
        self.s0 = s0

        # actually there's a list of lots of args, not packed
        self.lots_of_args = lots_of_args
        # actually kwargs are unpacked either,
        self.kwargs = kwargs

    def calc_price(self):
        price = ...
        return price

    def get_delta(self, ds=.05):
        cd = Option(self.s0 - ds, self.lots_of_args, **self.kwargs).calc_price()
        cu = Option(self.s0 + ds, self.lots_of_args, **self.kwargs).calc_price()   
        return (cu - cd) / (2 * ds)

The problem is that there are really a lot of args other than s0 (represented as lots_of_args, but actually they aren't a single arg, but there are a dozen of them), so I really hate to copy them one by one into the get_delta function. I need a way to get a dict of the args that are passed to __init__. The first thought, of course, is to use locals(). However, there're some problems:

  1. an extra self arg is included.

  2. kwargs are not unpacked.

  3. The doc on locals() advises against modifying the dict returned by locals(), so I don't think it's a good idea to del the self arg and update the unpacked kwargs.

Can anyone help? Thanks.


Solution

  • how about copying?

    import copy
    
    class Option:
        def __init__(self, s0, lots_of_args, **kwargs):
            self.s0 = s0
    
        def calc_price(self):
            price = ...
            return price
    
        def get_delta(self, ds=.05):
            cd = copy.copy(self)
            cd.s0 -= ds
            cu = copy.copy(self)
            cu.s0 += ds
    
            return (cu.calc_price() - cd.calc_price()) / (2 * ds)
    
    

    If your class has some dict/list attrs (or attrs using other mutable classes), then you might want to use copy.deepcopy instead (for example if calc_price() changes those attributes).

    And you can define __copy__ method to customize how copy is being created.