pythonarraysnumpyvectorizationarray-broadcasting

Can't vectorize this function - works with constants but returns ValueError operands could not be broadcast together


I wrote a python function I would expect to allow vectorization, using np.where and np.maximum. However, when attempting to call that function by passing dataframe columns, I get the error "ValueError: operands could not be broadcast together ..."

The function works fine when passing constants. I'm stuck on how to rewrite it to allow vectorization, ie accept pd.Series as arguments. I want to calculate the mean payoff for each row of the pandas.DataFrame, ie, an array of (2,1)

Update 2: Updated code slightly to fix a few issues

import numpy as np
import pandas as pd

np.random.seed(5)
times, dt = np.linspace(0, 1, 251, retstep=True)
B = np.random.normal(0, np.sqrt(dt), size=(100, 250)).T
S = np.exp((0 - .15 ** 2/ 2) * dt + .15 * B)
S = 4.5 * S.cumprod(axis=0)
df = pd.DataFrame({'days': [230,250], 'otype': ['c','p'], 'strike': [4.8,4.3], 'ko': [None, 'do'], 'b': [5,4.55]})

def montecarlo_payouts(montecarlo, j, opt, k, kotype = None, b = 0, i=1):
    #adjust daycount for index starting at 0
    i = i - 1
    j = j - 1
    #deal wih argument types
    opt = opt.str.lower() if isinstance(opt, pd.Series) else opt.lower()
    k = k.to_numpy() if isinstance(k, pd.Series) else k
    k = k[:,None] if isinstance(k, (pd.Series, np.ndarray)) else k

    #vanilla option payoffs for call and put
    conditions = [np.logical_or(opt == 'c',opt == 'call')]
    payoff = np.where(conditions, np.maximum(montecarlo[j] - k, 0), np.maximum(k - montecarlo[j], 0))
    return payoff.mean(axis=1)

df = pd.DataFrame({'days': [230,250], 'otype': ['c','p'], 'strike': [4.8,4.3]})
payout = montecarlo_payouts(S, 250, df['otype'], 5)

Solution

  • I was able to get it working using suggestions from @jared on another post and properly handling my series arguments.

    Working code:

    def montecarlo_payouts(montecarlo, j, opt, k, kotype = None, b = 0, i=1):
        #adjust daycount for index starting at 0
        j = j - 1
        #deal wih argument types
        j = j.to_numpy() if isinstance(j, pd.Series) else j
        opt = opt.str.lower().to_numpy() if isinstance(opt, pd.Series) else opt.lower()
        kotype = kotype.str.lower().to_numpy() if isinstance(kotype, pd.Series) else kotype.lower()
        k = k.to_numpy() if isinstance(k, pd.Series) else k
        k = k[:,None] if isinstance(k, np.ndarray) else k
        b = b.to_numpy() if isinstance(b, pd.Series) else b
        b = b[:,None] if isinstance(b, np.ndarray) else b
        kotype = kotype[:,None] if isinstance(kotype, np.ndarray) else kotype
    
        #vanilla option payoffs for call and put
        itm = montecarlo[j] - k
        conditions = [np.logical_or(opt == 'c',opt == 'call')]
        callorput = np.where(conditions, 1, -1)
        payoffs = np.maximum(itm * callorput.transpose(), 0)
        return payoffs