Ich bin ein Python-Neuling, also hoffe ich, dass meine zwei Fragen klar und vollständig sind. Ich habe den tatsächlichen Code und einen Testdatensatz im CSV-Format unten veröffentlicht.Erstelle mehrere Spalten in Pandas Dataframe von einer Funktion
Ich konnte den folgenden Code (meist mit Hilfe der StackOverflow-Mitwirkenden) erstellen, um die implizite Volatilität eines Optionskontrakts mit der Newton-Raphson-Methode zu berechnen. Der Prozess berechnet Vega bei der Bestimmung der impliziten Volatilität. Obwohl ich eine neue DataFrame-Spalte für Implied Volatility mithilfe der Pandas DataFrame-Methode apply erstellen kann, kann ich keine zweite Spalte für Vega erstellen. Gibt es eine Möglichkeit, zwei separate DataFrame-Spalten zu erstellen, wenn die Funktion IV & Vega zusammen zurückgibt?
Ich habe versucht:
return iv, vega
von Funktiondf[['myIV', 'Vega']] = df.apply(newtonRap, axis=1)
- Got
ValueError: Shape of passed values is (56, 2), indices imply (56, 13)
auch versucht:
return iv, vega
von Funktiondf['myIV'], df['Vega'] = df.apply(newtonRap, axis=1)
- Got
ValueError: Shape of passed values is (56, 2), indices imply (56, 13)
Zusätzlich ist der Berechnungsvorgang langsam. Ich habe numba importiert und den @ jit (nogil = True) Decorator implementiert, aber ich sehe nur eine Leistungsverbesserung von 25%. Der Testdatensatz ist der Leistungstest hat fast 900.000 Datensätze. Die Laufzeit beträgt 2 Stunden und 9 Minuten ohne Numba oder mit Numba, aber ohne Nogil = True. Die Laufzeit bei Verwendung von numba und @jit (nogil = True) beträgt 1 Stunde und 32 Minuten. Kann ich es besser machen?
from datetime import datetime
from math import sqrt, pi, log, exp, isnan
from scipy.stats import norm
from numba import jit
# dff = Daily Fed Funds (Posted rate is usually one day behind)
dff = pd.read_csv('https://research.stlouisfed.org/fred2/data/DFF.csv', parse_dates=[0], index_col='DATE')
rf = float('%.4f' % (dff['VALUE'][-1:][0]/100))
# rf = .0015 # Get Fed Funds Rate https://research.stlouisfed.org/fred2/data/DFF.csv
tradingMinutesDay = 450 # 7.5 hours per day * 60 minutes per hour
tradingMinutesAnnum = 113400 # trading minutes per day * 252 trading days per year
cal = USFederalHolidayCalendar() # Load US Federal holiday calendar
@jit(nogil=True) # nogil=True arg improves performance by 25%
def newtonRap(row):
"""Estimate Implied Volatility (IV) using Newton-Raphson method
:param row (dataframe): Options contract params for function
TimeStamp (datetime): Close date
Expiry (datetime): Option contract expiration date
Strike (float): Option strike
OptType (object): 'C' for call; 'P' for put
RootPrice (float): Underlying close price
Bid (float): Option contact closing bid
Ask (float): Option contact closing ask
:return:
float: Estimated implied volatility
"""
if row['Bid'] == 0.0 or row['Ask'] == 0.0 or row['RootPrice'] == 0.0 or row['Strike'] == 0.0 or \
row['TimeStamp'] == row['Expiry']:
iv, vega = 0.0, 0.0 # Set iv and vega to zero if option contract is invalid or expired
else:
# dte (Days to expiration) uses pandas bdate_range method to determine the number of business days to expiration
# minus USFederalHolidays minus constant of 1 for the TimeStamp date
dte = float(len(pd.bdate_range(row['TimeStamp'], row['Expiry'])) -
len(cal.holidays(row['TimeStamp'], row['Expiry']).to_pydatetime()) - 1)
mark = (row['Bid'] + row['Ask'])/2
cp = 1 if row['OptType'] == 'C' else -1
S = row['RootPrice']
K = row['Strike']
# T = the number of trading minutes to expiration divided by the number of trading minutes in year
T = (dte * tradingMinutesDay)/tradingMinutesAnnum
# TODO get dividend value
d = 0.00
iv = sqrt(2 * pi/T) * mark/S # Closed form estimate of IV Brenner and Subrahmanyam (1988)
vega = 0.0
for i in range(1, 100):
d1 = (log(S/K) + T * (rf - d + iv ** 2/2))/(iv * sqrt(T))
d2 = d1 - iv * sqrt(T)
vega = S * norm.pdf(d1) * sqrt(T)
model = cp * S * norm.cdf(cp * d1) - cp * K * exp(-rf * T) * norm.cdf(cp * d2)
iv -= (model - mark)/vega
if abs(model - mark) < 1.0e-9:
break
if isnan(iv) or isnan(vega):
iv, vega = 0.0, 0.0
# TODO Return vega with iv if add'l pandas column possible
# return iv, vega
return iv
if __name__ == "__main__":
# test function from baseline data
get_csv = True
if get_csv:
csvHeaderList = ['TimeStamp', 'OpraSymbol', 'RootSymbol', 'Expiry', 'Strike', 'OptType', 'RootPrice', 'Last',
'Bid', 'Ask', 'Volume', 'OpenInt', 'IV']
fileName = 'C:/tmp/test-20150930-56records.csv'
df = pd.read_csv(fileName, parse_dates=[0, 3], names=csvHeaderList)
else:
pass
start = datetime.now()
# TODO Create add'l pandas dataframe column, if possible, for vega
# df[['myIV', 'Vega']] = df.apply(newtonRap, axis=1)
# df['myIV'], df['Vega'] = df.apply(newtonRap, axis=1)
df['myIV'] = df.apply(newtonRap, axis=1)
end = datetime.now()
print end - start
Testdaten: C: /tmp/test-20150930-56records.csv
2015.09.30 16: 00: 00, AAPL151016C00109000, AAPL, 2015.10.16 16.00 Uhr: 00,109, C, 109,95,3,46,3,6,3,7,1565,1290,0,3497 2015-09-30 16: 00: 00, AAPL151016P00109000, AAPL, 2015-10-16 16: 00: 00,109, P, 109,95,2,4, 2.34,2,42,3790,308,0,3,06,10 2015-09-30 16: 00: 00, AAPL151016C00110000, AAPL, 2015-10-16 16: 00: 00,110, C, 109,95,3,2,86,3,10217,28850, 0.3288 2015-09-30 16: 00: 00, AAPL151016P00110000, AAPL, 2015-10-16 16: 00: 00,110, P, 109,95,2,81,2,74,2,8,12113,44427,0,3029 2015-09-30 16 : 00: 00, AAPL151016C00111000, AAPL, 2015-10-16 1 6: 00: 00,111, C, 109,95,2,35,2,44,2,45,6674,2318,0,3187 2015-09-30 16: 00: 00, AAPL151016P00111000, AAPL, 2015-10-16 16: 00: 00,111, P, 109,95,3,2,3,1,3,25,2031,3773,0,2926 2015-09-30 16: 00: 00, AAPL151120C00110000, AAPL, 2015-11-20 16: 00: 00,110, C, 109,95,5,9,5,7,5,95, 5330,17112,0,3635 2015-09-30 16: 00: 00, AAPL151120P00110000, AAPL, 2015-11-20 16: 00: 00,110, P, 109,95,6,15,6,1,6,3,3724,15704,0,3842
Danke, BrenBarn. Das hat perfekt funktioniert. – vlmercado