背景
我想解决各种优化问题,例如投资组合中的资产权重,以及交易策略中的参数,其中变量也传递给包含一堆其他变量的函数。
到目前为止,我已经能够使用Solver加载项在Excel中轻松完成这些工作。但我认为使用Python会更有效,甚至可以更广泛地应用。为了清楚起见,我将把问题归结为投资组合优化的本质。
我的问题(简短版):
这是一个数据框和带有资产回报的相应图。
Dataframe 1:
A1 A2
2017-01-01 0.0075 0.0096
2017-01-02 -0.0075 -0.0033
.
.
2017-01-10 0.0027 0.0035
图1 - 资产回报
基于此,我想找到关于risk / return (Sharpe ratio)
的最佳投资组合的权重,由下图中的绿点表示(红点是所谓的最小方差投资组合,以及代表另一个优化问题。)
Plot 2 - 高效的前沿和最佳投资组合:
详细信息:
以下代码部分包含函数returns()
,用于构建具有两个资产的随机返回的数据帧,以及函数pf_sharpe
,用于计算两个给定权重的夏普比率。回报。
# imports
import pandas as pd
import numpy as np
from scipy.optimize import minimize
import matplotlib.pyplot as plt
np.random.seed(1234)
# Reproducible data sample
def returns(rows, names):
''' Function to create data sample with random returns
Parameters
==========
rows : number of rows in the dataframe
names: list of names to represent assets
Example
=======
>>> returns(rows = 2, names = ['A', 'B'])
A B
2017-01-01 0.0027 0.0075
2017-01-02 -0.0050 -0.0024
'''
listVars= names
rng = pd.date_range('1/1/2017', periods=rows, freq='D')
df_temp = pd.DataFrame(np.random.randint(-100,100,size=(rows, len(listVars))), columns=listVars)
df_temp = df_temp.set_index(rng)
df_temp = df_temp / 10000
return df_temp
# Sharpe ratio
def pf_sharpe(df, w1, w2):
''' Function to calculate risk / reward ratio
based on a pandas dataframe with two return series
Parameters
==========
df : pandas dataframe
w1 : portfolio weight for asset 1
w2 : portfolio weight for asset 2
'''
weights = [w1,w2]
# Calculate portfolio returns and volatility
pf_returns = (np.sum(df.mean() * weights) * 252)
pf_volatility = (np.sqrt(np.dot(np.asarray(weights).T, np.dot(df.cov() * 252, weights))))
# Calculate sharpe ratio
pf_sharpe = pf_returns / pf_volatility
return pf_sharpe
# Make df with random returns and calculate
# sharpe ratio for a 80/20 split between assets
df_returns = returns(rows = 10, names = ['A1', 'A2'])
df_returns.plot(kind = 'bar')
sharpe = pf_sharpe(df = df_returns, w1 = 0.8, w2 = 0.2)
print(sharpe)
# Output:
# 5.09477512073
现在我想找到优化夏普比率的投资组合权重。我想你可以表达优化问题如下:
maximize:
pf_sharpe()
by changing:
w1, w2
under the constraints:
0 < w1 < 1
0 < w2 < 1
w1 + w2 = 1
到目前为止我尝试过的事情:
我在帖子Python Scipy Optimization.minimize using SLSQP showing maximized results中找到了可能的设置。以下是我到目前为止的内容,它直接解决了我的问题的核心方面:
[...]将变量传递给包含一堆其他变量的函数。
正如您所看到的,我的初始挑战阻止我甚至测试我的边界和约束是否会被函数optimize.minimize()
接受。我甚至不愿意考虑到这是一个最大化而不是最小化问题的事实(希望通过改变函数的符号来修改)。
尝试:
# bounds
b = (0,1)
bnds = (b,b)
# constraints
def constraint1(w1,w2):
return w1 - w2
cons = ({'type': 'eq', 'fun':constraint1})
# initial guess
x0 = [0.5, 0.5]
# Testing the initial guess
print(pf_sharpe(df = df_returns, weights = x0))
# Optimization attempts
attempt1 = optimize.minimize(pf_sharpe(), x0, method = 'SLSQP', bounds = bnds, constraints = cons)
attempt2 = optimize.minimize(pf_sharpe(df = df_returns, weights), x0, method = 'SLSQP', bounds = bnds, constraints = cons)
attempt3 = optimize.minimize(pf_sharpe(weights, df = df_returns), x0, method = 'SLSQP', bounds = bnds, constraints = cons)
结果:
df
和weights
都没有指定。SyntaxError: positional argument follows keyword argument
NameError: name 'weights' is not defined
我的印象是df
可以自由指定,x0
optimize.minimize
中的pf_sharpe()
将被视为要测试的变量&#39;代表&#39}。对于# Portfolio simulation
def portfolioSim(df, simRuns):
''' Function to take a df with asset returns,
runs a number of simulated portfolio weights,
plots return and risk for those weights,
and finds minimum risk portfolio
and max risk / return portfolio
Parameters
==========
df : pandas dataframe with returns
simRuns : number of simulations
'''
prets = []
pvols = []
pwgts = []
names = list(df_returns)
for p in range (simRuns):
# Assign random weights
weights = np.random.random(len(list(df_returns)))
weights /= np.sum(weights)
weights = np.asarray(weights)
# Calculate risk and returns with random weights
prets.append(np.sum(df_returns.mean() * weights) * 252)
pvols.append(np.sqrt(np.dot(weights.T, np.dot(df_returns.cov() * 252, weights))))
pwgts.append(weights)
prets = np.array(prets)
pvols = np.array(pvols)
pwgts = np.array(pwgts)
pshrp = prets / pvols
# Store calculations in a df
df1 = pd.DataFrame({'return':prets})
df2 = pd.DataFrame({'risk':pvols})
df3 = pd.DataFrame(pwgts)
df3.columns = names
df4 = pd.DataFrame({'sharpe':pshrp})
df_temp = pd.concat([df1, df2, df3, df4], axis = 1)
# Plot resulst
plt.figure(figsize=(8, 4))
plt.scatter(pvols, prets, c=prets / pvols, cmap = 'viridis', marker='o')
# Min risk
min_vol_port = df_temp.iloc[df_temp['risk'].idxmin()]
plt.plot([min_vol_port['risk']], [min_vol_port['return']], marker='o', markersize=12, color="red")
# Max sharpe
max_sharpe_port = df_temp.iloc[df_temp['sharpe'].idxmax()]
plt.plot([max_sharpe_port['risk']], [max_sharpe_port['return']], marker='o', markersize=12, color="green")
# Test run
portfolioSim(df = df_returns, simRuns = 250)
指定的函数中的权重。
正如您当然明白的那样,我在这方面从Excel到Python的过渡并不是最容易的,而且我在这里也不了解。无论如何,我希望你们中的一些人可以提供一些建议或澄清!
谢谢!
附录1 - 模拟方法:
通过模拟一系列投资组合权重,可以轻松解决此特定投资组合优化问题。我正是这样做的,以产生上面的投资组合图。如果有人有兴趣,这就是整个功能:
C3 =AVERAGE(C7:C16)
C4 =AVERAGE(D7:D16)
H4 =COVARIANCE.P(C7:C16;D7:D16)
G5 =COVARIANCE.P(C7:C16;D7:D16)
G10 =G8+G9
G13 =MMULT(TRANSPOSE(G8:G9);C3:C4)
G14 =SQRT(MMULT(TRANSPOSE(G8:G9);MMULT(G4:H5;G8:G9)))
H13 =G12/G13
H14 =G13*252
G16 =G13/G14
H16 =H13/H14
附录2 - Excel解算器方法:
以下是使用Excel Solver解决问题的方法。我没有链接到文件,而是附加了屏幕截图,并在代码部分中包含了最重要的公式。我猜你们当中很多人都不会有兴趣再现这个。但我之所以加入它只是为了表明它可以在Excel中轻松完成。 灰度范围代表公式。可以更改并在优化问题中用作参数的范围以黄色突出显示。绿色范围是目标函数。
这是工作表和解算器设置的图像:
Excel公式:
47% / 53%
结束说明:
从截图中可以看出,Excel解算器建议在A1和A2之间进行sr_opt = portfolioSim(df = df_returns, simRuns = 25000)
分割,以获得最佳的夏普比率为5,6。运行Python函数46% and 53%
产生的夏普比率为5,3,A1和A2的权重为print(sr_opt)
#Output
#return 0.361439
#risk 0.067851
#A1 0.465550
#A2 0.534450
#sharpe 5.326933
:
GRG Nonlinear
Excel中应用的方法是SLSQP
。我知道将{{1}}参数更改为非线性方法会让我处于某个位置,而且我也会查看Nonlinear solvers in scipy,但收效甚微。
也许Scipy甚至不是最好的选择吗?
答案 0 :(得分:1)
更详细的答案,代码的第一部分保持不变
pf_sharpe
修改了函数w1 + w2 = 1
,第一个输入是权重之一,即要优化的参数。我们可以在w2
内将1-w1
定义为pf_sharpe
,而不是输入约束minimize
,这完全相同,但更简单,更快捷。此外,pf_sharpe
会尝试最小化pf_sharpe
,并且您实际上想要最大化它,所以现在# Sharpe ratio
def pf_sharpe(weight, df):
''' Function to calculate risk / reward ratio
based on a pandas dataframe with two return series
'''
weights = [weight[0], 1-weight[0]]
# Calculate portfolio returns and volatility
pf_returns = (np.sum(df.mean() * weights) * 252)
pf_volatility = (np.sqrt(np.dot(np.asarray(weights).T, np.dot(df.cov() * 252, weights))))
# Calculate sharpe ratio
pf_sharpe = pf_returns / pf_volatility
return -pf_sharpe
# initial guess
x0 = [0.5]
df_returns = returns(rows = 10, names = ['A1', 'A2'])
# Optimization attempts
out = minimize(pf_sharpe, x0, method='SLSQP', bounds=[(0, 1)], args=(df_returns,))
optimal_weights = [out.x, 1-out.x]
print(optimal_weights)
print(-pf_sharpe(out.x, df_returns))
的输出乘以-1。
registerUser(phoneNumber: number) {
alert('flat' + this.flatn.value);
var q = firebase.database().ref('users').orderByChild('flatno').equalTo(this.flatn.value);
q.once('value', (snapshots: any) => {
// alert(JSON.stringify(snapshots.val()));
this.len = snapshots.numChildren();
alert('len ='+this.len);
if(this.len < 2){
this.alert('success');
// i wanted to register user only if this condition gets true. but my code is not being perform as i want which is written after if condition.
}else{
//this.alert('success');
this.alert('More than 2 users are not allowed to register with same flat');
// flatno does not yet exist, go ahead and add new user
}
this.fire.auth
.createUserWithEmailAndPassword(this.email.value, this.password.value)
.then(data => {
let currentUserUid = this.fire.auth.currentUser.uid;
this.uniqueDeviceID.get()
.then((uDid: any) => this.uDid = uDid)
.catch((error: any) => alert('err' + error));
alert(this.uDid);
firebase.database().ref('users/' + currentUserUid).set({
ID: currentUserUid,
email: this.email.value,
password: this.password.value,
first_name: this.fname.value,
last_name: this.lname.value,
contact_no: this.phone.value,
flatno: this.flatn.value,
wing: this.wing.value,
parking_slot: this.vehicle.value,
familyMember: this.familyMember.value,
username: this.user.value,
device_id: this.uDid
});
this.fdb.list("/users_device/").push({
device_id: this.uDid,
Uid: currentUserUid
});
console.log("got data ", data);
//this.alert(data);
this.alert("Registered!");
data.sendEmailVerification().then(
function () {
this.alert("Email Sent Please check your mailbox!");
},
function (error) {
alert("error!");
}
);
this.navCtrl.push(LoginPage);
if (this.authForm.valid) {
let loader = this.loadingCtrl.create({
content: 'Registering...',
dismissOnPageChange: true
});
loader.present();
this.navCtrl.push(LoginPage);
}
})
.catch(error => {
console.log("got an error ", error);
this.alert(error.message);
});
});
}
这使得优化的夏普比率为6.16(优于5.3),对于w1几乎为1,w2几乎为0