Python class with functions that call and minimize other functions - python

I am trying to write a function within a class that is trying to minimise another function.
What is the correct way to write and call it ? I am stuck and I am not sure how to do this.
import pandas as pd
import scipy.optimize as sco
class Optimisation:
def __init__(self, rf, expected_return, cov):
self.rf = rf
self.expected_return = expected_return
self.cov = cov
def calculate_negative_sharpe(self):
self.portfolio_return = np.sum(expected_returns * weights) * 252
self.portfolio_std = np.sqrt(np.dot(self.weights.T, np.dot(self.cov, self.weights))) * np.sqrt(252)
self.sharpe_ratio = (self.portfolio_return - self.rf) / self.portfolio_std
return -self.sharpe_ratio
def max_sharpe_ratio(expected_returns, cov, rf):
num_assets = len(expected_returns)
args = (expected_returns, cov, rf)
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
bound = (0.0,1.0)
bounds = tuple(bound for asset in range(num_assets))
result = sco.minimize(calculate_negative_sharpe, num_assets*[1./num_assets,], args=args, method='SLSQP', bounds=bounds, constraints=constraints)
return result
opt = Optimisation(rf, er, cov)
result = opt.max_sharpe_ratio()
print(result)
or should it be like
import pandas as pd
import scipy.optimize as sco
class Optimisation:
def __init__(self, rf, expected_return, cov):
self.rf = rf
self.expected_return = expected_return
self.cov = cov
def calculate_negative_sharpe(self):
self.portfolio_return = np.sum(expected_returns * weights) * 252
self.portfolio_std = np.sqrt(np.dot(self.weights.T, np.dot(self.cov, self.weights))) * np.sqrt(252)
self.sharpe_ratio = (self.portfolio_return - self.rf) / self.portfolio_std
return -self.sharpe_ratio
def max_sharpe_ratio(self):
self.num_assets = len(self/expected_returns)
self.args = (self.expected_returns, self.cov, self.rf)
self.constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
self.bound = (0.0,1.0)
self.bounds = tuple(self.bound for asset in range(self.num_assets))
self.result = sco.minimize(self.calculate_negative_sharpe, self.num_assets*[1./self.num_assets,], args=self.args, method='SLSQP', bounds=self.bounds, constraints=self.constraints)
return self.result
opt = Optimisation(rf, er, cov)
result = opt.max_sharpe_ratio()
print(result)
My goal is to return a result dataframe which I can print or save.

Related

Where is the value in the lambda function coming from?

below is the code part from this github repo that I am confused of:
full_promp.py:
.....
......
class ProbInvKinematics:
#params:
#fwd_k: A forward kinematics object
def __laplace_cost_and_grad(self, theta, mu_theta, inv_sigma_theta, mu_x, inv_sigma_x):
print ("theta ",theta)
f_th, jac_th, ori = self.fwd_k.position_and_jac(theta)
jac_th = jac_th[0:3,:]
diff1 = theta - mu_theta
tmp1 = np.dot(inv_sigma_theta, diff1)
diff2 = f_th - mu_x
tmp2 = np.dot(inv_sigma_x, diff2)
nll = 0.5*(np.dot(diff1,tmp1) + np.dot(diff2,tmp2))
grad_nll = tmp1 + np.dot(jac_th.T,tmp2)
return nll, grad_nll
def __init__(self, fwd_kinematics):
self.fwd_k = fwd_kinematics
def inv_kin(self, mu_theta, sig_theta, mu_x, sig_x):
inv_sig_theta = np.linalg.inv(sig_theta)
inv_sig_x = np.linalg.inv(sig_x)
cost_grad = lambda theta: self.__laplace_cost_and_grad(theta, mu_theta, inv_sig_theta, mu_x, inv_sig_x)
cost = lambda theta: cost_grad(theta)[0]
grad = lambda theta: cost_grad(theta)[1]
res = opt.minimize(cost, mu_theta, method='BFGS', jac=grad)
post_mean = res.x
post_cov = res.hess_inv
return post_mean, post_cov
Usage of the class ProbInvKinematics as follow:
import robpy.full_promp as promp
prob_inv_kin = promp.ProbInvKinematics(fwd_kin)
mu_cartesian = np.array([-0.62, -0.44, -0.34])
Sigma_cartesian = 0.02**2*np.eye(3)
mu_q, Sigma_q = prob_inv_kin.inv_kin(mu_theta=prior_mu_q, sig_theta=prior_Sigma_q,
mu_x = mu_cartesian, sig_x = Sigma_cartesian)
I see that the parameter value theta is defined nowhere. But somehow when I try to print out theta in def __laplace_cost_and_grad(), the value is there... What is the logic of using this theta?

Fitting model to data using scipy differential evolution: "RuntimeError: The map-like callable must be of the form f(func, iterable)..."

I am trying to fit a model to data (extracted from an Excel file and imported using pandas), using a likelihood method. However, when running the code I get a "RuntimeError: The map-like callable must be of the form f(func, iterable), returning a sequence of numbers the same length as 'iterable'" error, which occurred at the "result_simul_G = minimize(negLogLike, params, method = 'differential_evolution', args=(x, y),)" line. Below I have my code; it's very integrated so I couldn't find a way to illustrate what's happening without showing most of it.
#================================================================================
import numpy as np
import pandas as pd
import os
from lmfit import minimize, Parameters, Parameter, report_fit
params = Parameters()
params.add('gamma', value=.45, min=0, max=1, vary = True)
params.add('n', value = 1, min=0, max=3, vary = True)
filename = 'data.xlsx'
#================================================================================
def negLogLike(params, xData, yData):
new_xData = []
new_yData = []
for i in range(len(yData)):
if ((yData[i] != 0) and (xData[i] != 0)):
new_xData.append(xData[i])
new_yData.append(yData[i])
model_result = model(new_xData, params)
nll = 0
epsilon = 10**-10
for i in range(len(new_yData)):
if (model_result[i] < epsilon):
model_result[i] = epsilon
if (model_result[i] > 1 - epsilon):
model_result[i] = 1 - epsilon
nll += new_yData[i] * np.log(model_result[i]) + (1 - new_yData[i]) * np.log(1 - model_result[i])
return -nll
#================================================================================
def model(x, params):
try: # Get parameters
g = params['gamma'].value
n = params['n'].value
except KeyError:
g, n = params
y = 1 - np.exp(-g * x**n)
return y
#================================================================================
def GetFits(DataFrame):
cell_count = 2300000
GFP_GC_SIMUL = np.ones(DataFrame.shape[0], float)
GFP_IC_SIMUL = np.ones(DataFrame.shape[0], float)
# Data
for i in range(DataFrame.shape[0]):
GFP_GC_SIMUL[i] = DataFrame.loc[i, 'GFP genomes'] / cell_count
GFP_IC_SIMUL[i] = DataFrame.loc[i, 'GFP IU'] / cell_count
x = np.array(GFP_GC_SIMUL[10:-10])
y = np.array(GFP_IC_SIMUL[10:-10])
print('len=', len(x), x.dtype, ', x=', x)
print('------------------------')
print('len=', len(y), y.dtype, ', y=', y)
result_simul_G = minimize(negLogLike, params, method = 'differential_evolution', args=(x, y),)
#================================================================================
DataFrame = pd.read_excel('data.xlsx', engine='openpyxl')
GetFits(DataFrame)
When debugging on my own I used print statements to see what x and y data was being supplied to the minimizer and this is what it showed:
len= 34 float64 , x= [0.14478261 0.28695652 0.28695652 0.28695652 0.57391304 0.57391304
0.57391304 0.8738913 0.8738913 0.8738913 1.16086957 1.16086957
1.16086957 1.44780435 1.44780435 1.44780435 1.73478261 1.73478261
1.73478261 2.03476087 2.03476087 2.03476087 2.32173913 2.32173913
2.32173913 2.60869565 2.60869565 2.60869565 2.86956522 2.86956522
2.86956522 7.17391304 7.17391304 7.17391304]
------------------------
len= 34 float64 , y= [0.005 0.01180435 0.01226087 0.01158696 0.036 0.03704348
0.03467391 0.07030435 0.06556522 0.07567391 0.1001087 0.09852174
0.0986087 0.13626087 0.13978261 0.13956522 0.16847826 0.16408696
0.19391304 0.1945 0.21319565 0.19052174 0.32204348 0.23330435
0.25028261 0.28136957 0.26293478 0.25893478 0.28273913 0.29717391
0.273 0.60826087 0.60834783 0.59482609]
I know this is quite a lot but I would appreciate any and all help.

Fetch argument None has invalid type <class 'NoneType'> when call tf.gradients

I am creating a monte carlo simulation to price option via tensorflow. When I try to call second gradient to calculate gamma, it threw an error that:
Fetch argument None has invalid type <class 'NoneType'>
When I run the code with blstfPricer:
pricerobj = OptionPricer()
blspricer = pricerobj.blstfPricer()
price = blspricer(100,110,2,0.2,0.03,0)
Everything works well. But when I call:
pricer = pricerobj.tfmcPricer(TFMonteCarloSimulatorType.GBM,enable_greeks = True)
price = pricer(100,110,2.0,0.2,0.03,0,0,100000,365 * 2)
It will throw an error that:
Fetch argument None has invalid type <class 'NoneType'>
I checked that the issue was caused by
gamma = tf.gradients(greeks[0],[spot])
Some one can help me with it?
Please see my code below:
class TFMonteCarloSimulator(object):
def __init__(self, SimulatorType):
if not isinstance(SimulatorType, TFMonteCarloSimulatorType):
raise Exception("Please use defined Simulator Type from class TFMonteCarloSimulatorType")
self.simulatorType = SimulatorType
def _GBMSimulation(self):
spot = tf.placeholder(tf.float32)
strike = tf.placeholder(tf.float32)
dt = tf.placeholder(tf.float32)
vol = tf.placeholder(tf.float32)
timeToMaturity = tf.placeholder(tf.float32)
dw = tf.placeholder(tf.float32)
miu = tf.placeholder(tf.float32)
div = tf.placeholder(tf.float32)
pricer = spot * tf.cumprod(tf.exp((miu - div - (vol ** 2) / 2) * dt + vol * tf.sqrt(dt) * dw), axis=1)
return (spot, strike, dt, vol, miu, div, dw, pricer,timeToMaturity)
def createSimulator(self):
if self.simulatorType == TFMonteCarloSimulatorType.GBM:
(spot, strike, dt, vol, miu, div, dw, pricer,timeToMaturity) = self._GBMSimulation()
def simulator(S, K, TTM, volatility, drift, dividend, seed, Nsimulation, Ndays):
if seed != 0:
np.random.seed(seed)
rndMatrix = np.random.randn(Nsimulation, Ndays)
with tf.Session() as sess:
timeDelta = TTM / Ndays
res = sess.run(pricer,{
spot: S,
strike: K,
miu:drift,
div:dividend,
vol:volatility,
dt:timeDelta,
timeToMaturity:TTM,
dw:rndMatrix
})
return res
return simulator
else:
return None
class OptionPricer(TFMonteCarloSimulator):
def __init__(self):
pass
def blstfPricer(self, enable_greeks = True):
spot = tf.placeholder(tf.float32)
strike = tf.placeholder(tf.float32)
dt = tf.placeholder(tf.float32)
vol = tf.placeholder(tf.float32)
ir = tf.placeholder(tf.float32)
div = tf.placeholder(tf.float32)
ndf = tfp.distributions.Normal(0,1).cdf
d1 = (tf.log(spot/strike) + (ir + (vol ** 2)/2) * dt) / (vol * tf.sqrt(dt))
d2 = d1 - vol * tf.sqrt(dt)
price = spot * tf.exp(-div * dt) * ndf(d1) - strike * tf.exp(-ir * dt) * ndf(d2)
targets = [price]
if enable_greeks:
greeks = tf.gradients(price, [spot, vol, ir, dt])
gamma = tf.gradients(ys = greeks[0],xs = [spot])
targets += [greeks, gamma]
def execute(S,K, TTM, imVol, IR, Div):
with tf.Session() as sess:
res = sess.run(targets,
{spot:S,
strike:K,
ir:IR,
vol:imVol,
div:Div,
dt:TTM
})
return res
return execute
def tfmcPricer(self, simType,enable_greeks = True):
if simType == TFMonteCarloSimulatorType.GBM:
(spot, strike, dt, vol, miu, div, dw, pricer, timeToMaturity) = super(OptionPricer, self)._GBMSimulation()
payoff = tf.maximum(pricer[:,-1] - strike, 0)
price = tf.exp(-miu * timeToMaturity) * tf.reduce_mean(payoff)
targets = [price]
if enable_greeks:
greeks = tf.gradients(price, [spot, vol, miu,timeToMaturity])
gamma = tf.gradients(greeks[0],[spot])
targets += [greeks]
targets += [gamma]
def execute(S, K, TTM, volatility, drift, dividend,seed, Nsimulation,Ndays):
if seed != 0:
np.random.seed(seed)
rndMatrix = np.random.randn(Nsimulation, Ndays)
with tf.Session() as sess:
timedelta = TTM / Ndays
res = sess.run(targets,
{
timeToMaturity:TTM,
spot:S,
strike:K,
dt:timedelta,
vol:volatility,
miu:drift,
div:dividend,
dw:rndMatrix
}
)
return res
return execute
else:
return None

Finding Where Put Option Equals K-S, using the Black-Sholes, throws Exceptions. Why?

I am trying to find the value of the S where a Put option equals K-S in Python, where K is the strike price of the option and S is the underlying strike price. Also, in the function call of the Black-Sholes, sigma is the volatility, delta is the dividends paid, T is the time to expiration, r is the risk-free rate of interest.
I am testing it with K=75, r=0.05, T=1/12, sigma =0.35.
Furthermore, I know that my pricing for the Black-Sholes works as I have used it in a previous project of mine, it is possible that the modified versions of a syntax error.
I have tried using scipy.optimize, but I keep getting errors.
Is there another method, I should use?
If so, how?
import numpy as np
import scipy.stats as ss
import time
import pylab as pl
import matplotlib.pyplot as plt
from prettytable import PrettyTable
import datetime
import scipy.optimize
from scipy.optimize import newton
# initial stock price
type = 'C'
def d1(S0, K, r, sigma, T, delta):
return (np.log(float(S0) / K) + (r - delta + sigma ** 2 / 2) * T) / (sigma * np.sqrt(T))
def d2(S0, K, r, sigma, T,delta):
return (d1(S0, K, r, sigma, T,delta)-sigma * np.sqrt(T))
def blackScholes(type, S0, K, r, sigma, T, delta):
if type == "C":
return S0 * np.exp(-delta * T)* ss.norm.cdf(d1(S0, K, r, sigma, T, delta)) - K * np.exp(-r * T) * ss.norm.cdf(d2(S0, K, r, sigma, T, delta))
else:
return K * np.exp(-r * T) * ss.norm.cdf(-d2(S0, K, r, sigma, T, delta)) - S0 * ss.norm.cdf(-d1(S0, K, r, sigma, T, delta))
# args is args = (type,K,r,sigma,T,delta)
# Modifying Black-Sholes function arguments for multivariate optimization
def d1Modified(S, args):
type = args[0]
K = args[1]
r = args[2]
sigma = args[3]
T = args[4]
delta = args[5]
return (np.log(float(S) / K) + (r - delta + sigma ** 2 / 2) * T) / (sigma * np.sqrt(T))
def d2Modified(S, args):
type = args[0]
K = args[1]
r = args[2]
sigma = args[3]
T = args[4]
delta = args[5]
return (d1Modified(S,args) - sigma * np.sqrt(T))
def blackScholesModified(S, args):
type = args[0]
print("print args")
print(args)
K = args[1]
r = args[2]
sigma = args[3]
T = args[4]
delta = args[5]
if type == "C":
return S * np.exp(-delta * T) * ss.norm.cdf(d1Modified(S, args)) - K * np.exp(
-r * T) * ss.norm.cdf(d2Modified(S,args))
else:
return K * np.exp(-r * T) * ss.norm.cdf(-d2Modified(S, args)) - S * ss.norm.cdf(
-d1Modified(S,args))
print("Pricing a European Put Option")
p = "P"
#Note: at is the tuple with the inputs into the black-sholes equation
# Where p is a string indicating a put option, K is the strike price
# r is the risk free rate of interest, sigma is volatility, T is time to
# expiration and delta is dividends
ar = (p,K,r,sigma,T,delta)
putOptionPriceList = []
for i in range(1,74):
stockPriceList.append(i)
for x in stockPriceList:
price = blackScholes("P",x,K,r,sigma,T,delta)
print(price)
putOptionPriceList.append(price)
# Constraints for multivariate optimization where price = K-S
def con(S,ar):
k= 75
return blackScholesModified(S, ar) -(k-S)
cons = {'type':'eq', 'fun': con(S,ar)}
sPrice = scipy.optimize.minimize(blackScholesModified, 0.1, args=ar, constraints=cons)
print("Value sought")
print(sPrice)
I keep getting the following error:
Traceback (most recent call last):
sPrice = scipy.optimize.minimize(blackScholesModified, 0.1, args=ar, constraints=cons)
File "C:\Users\user\Anaconda3\lib\site-packages\scipy\optimize\_minimize.py", line 458, in minimize
constraints, callback=callback, **options)
File "C:\Users\user\Anaconda3\lib\site-packages\scipy\optimize\slsqp.py", line 311, in _minimize_slsqp
meq = sum(map(len, [atleast_1d(c['fun'](x, *c['args'])) for c in cons['eq']]))
File "C:\Users\user\Anaconda3\lib\site-packages\scipy\optimize\slsqp.py", line 311, in <listcomp>
meq = sum(map(len, [atleast_1d(c['fun'](x, *c['args'])) for c in cons['eq']]))
TypeError: 'numpy.float64' object is not callable
My guess would be to carefully follow the 0.18 specified calling interface:
cons = { 'type': 'eq',
'fun': con, # con( S, ar ) here'd yield float64, not callable
'args': ( S, ar ) # ( ^__^__)_ passed to a fun == con()-callable
}

Optimizing performance of SciPy Minimize while using Concurrent.Futures?

I'm trying to figure out how to speed up a scipy.minimize function.
minimize() is called thousands of times. I run it in parallel using a ProcessPoolExecutor. bootstrap() is the parent function.
def optimize_weights(forecasts, prices, instrument):
guess = [1/forecasts.shape[1]] * forecasts.shape[1]
bounds = [(0.0,1.0)] * forecasts.shape[1]
cons = {'type': 'eq', 'fun': lambda x: 1 - sum(x)}
def function(w, forecasts, prices, instrument):
wf = (w*forecasts).mean(axis=1)
wf = wf*10/wf.std()
wf = wf.clip(-20,20)
l = accountCurve(wf, prices, slippage=instrument.slippage, per_contract_cost=instrument.per_contract_cost)
return -l.sharpe()
result = minimize(function, guess, (forecasts, prices, instrument), bounds=bounds, method='SLSQP', tol=0.0001, constraints=cons, options={'disp': False ,'eps' : 1e0})
return result.x
def mp_optimize_weights(samples, prices, instrument):
with ProcessPoolExecutor() as executor:
return executor.map(partial(optimize_weights, prices=prices, instrument=instrument), samples)
def bootstrap(instrument, parallel_process=True):
print("Parallel Process: ", parallel_process)
forecasts = instrument.forecasts().dropna()
prices = instrument.prices().reset_index('Contract', drop=True)
prices = prices[forecasts.index]
years = list(set(prices.index.year))
years.sort()
result={}
for year in years:
sample_length = np.int(prices[:str(year)].size/10)
end_of_sample_selection_space = prices[:str(year)].tail(1).index[0] - pd.Timedelta(days=sample_length)
sample_dates = pd.to_datetime(np.random.choice(prices[:end_of_sample_selection_space].index,100))
if(sample_length > 50):
samples = [forecasts.loc[date:date+pd.Timedelta(days=sample_length)] for date in sample_dates]
if parallel_process is True:
weights = pd.DataFrame(list(mp_optimize_weights(samples, prices[:str(year)], instrument=instrument)))
else:
weights = pd.DataFrame(list(map(partial(optimize_weights, prices=prices[:str(year)], instrument=instrument), samples)))
if len(weights)<2:
print('Weights error')
break
result[year]=weights.mean()
print(year, sample_length)
output = pd.DataFrame.from_dict(result).transpose()
output.columns = forecasts.columns
pl.plot(output)
display.clear_output(wait=True)
display.display(pl.gcf())
return output
import numpy as np
import pandas as pd
class accountCurve():
def __init__(self, forecasts, prices, annual_volatility_target=0.25, multiplier = 1, per_contract_cost = 0, slippage = 0, capital=100000, costs=True):
if prices.index.names[0] == 'Contract':
prices = prices.reset_index('Contract', drop=True)
#Adjust for contract multiplier/pricing in pennies
prices = prices*multiplier/100
self.prices = prices
daily_volatility_target = annual_volatility_target/np.sqrt(252)
instrument_value_volatility = prices.diff().ewm(span=36, min_periods=36).std()
self.notional_position = (forecasts * daily_volatility_target * capital).divide(10.0 * instrument_value_volatility[forecasts.index], axis=0)
self.notional_position.dropna(inplace=True)
#Chunk trades to be at least 10% move in position (to reduce trading costs)
self.position = chunk_trades(self.notional_position)
#Round positions to integers
self.position = np.around(self.notional_position)
#self.position.mark_to_market = self.position.multiply(prices, axis=0)
self.trades = self.position.diff()
#Calculate returns
self.gross_returns = (prices.diff().shift(-1)*self.position).dropna()
if costs:
self.costs = self.trades.abs() * per_contract_cost
self.slippage = self.trades.abs() * slippage * multiplier/100
self.returns = (self.gross_returns - self.slippage - self.costs).dropna()
else:
self.returns = self.gross_returns
def sharpe(self):
return self.returns.mean()/self.returns.std()*np.sqrt(252)
def losses(self):
return [z for z in self.returns if z<0]
def sortino(self):
returns = self.returns.pct_change()
return returns.mean()/np.std(losses(returns))*np.sqrt(252)
def plot(self):
self.returns.cumsum().plot()
def chunk_trades(A):
#Take a list of notional positions and filter so that trades are only greater than 10% of notional position
last = A[0]
new = []
for x in A.iteritems():
if np.abs((x[1]-last)/last) > 0.1:
new.append(x[1])
last = x[1]
else:
new.append(last)
s = pd.Series(new, index=A.index)
return s
On my data, this takes around 45 minutes to run.
I'd like to know:
Is my approach to parallel processing correct? Should I be using threads instead of processes?
Can I reconfigure minimize to finish faster? This is bootstrapping, which is a monte-carlo based sampling method, may not require such an accurate result.
Anything else I can do to speed it up?
In an ideal world, I'd like to speed it up an order of magnitude.

Categories