优化函数参数

时间:2018-04-09 11:00:48

标签: python numpy optimization scipy

背景

我想解决各种优化问题,例如投资组合中的资产权重,以及交易策略中的参数,其中变量也传递给包含一堆其他变量的函数

到目前为止,我已经能够使用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 - 资产回报

enter image description here

基于此,我想找到关于risk / return (Sharpe ratio)的最佳投资组合的权重,由下图中的绿点表示(红点是所谓的最小方差投资组合,以及代表另一个优化问题。)

Plot 2 - 高效的前沿和最佳投资组合:

enter image description here

如何使用numpy或scipy执行此操作?

详细信息:

以下代码部分包含函数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)

结果:

  • Attempt1最接近scipy设置here,但可以理解的是因为dfweights都没有指定。
  • Attempt2因SyntaxError: positional argument follows keyword argument
  • 而失败
  • Attempt3因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中轻松完成。 灰度范围代表公式。可以更改并在优化问题中用作参数的范围以黄色突出显示。绿色范围是目标函数。

这是工作表和解算器设置的图像:

enter image description here

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甚至不是最好的选择吗?

1 个答案:

答案 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