このページは、nAGライブラリのJupyterノートブックExampleの日本語翻訳版です。オリジナルのノートブックはインタラクティブに操作することができます。
nAGライブラリを使用した二次錐計画法(SOCP)によるポートフォリオ最適化のモデリング技法
このノートブックの正しいレンダリング
このノートブックでは、方程式と参照のためにlatex_envs
Jupyter拡張機能を使用しています。LaTeXがローカルのJupyterインストールで適切にレンダリングされない場合は、この拡張機能をインストールしていない可能性があります。詳細は
https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/nbextensions/latex_envs/README.html
を参照してください。
また、このノートブックはGitHubでは適切にレンダリングされないため、GitHubで閲覧している場合は、PDF版を参照することをお勧めします。
nAGライブラリMark (27.1)以降のユーザーへの注意
nAGライブラリのMark
はじめに
二次錐計画法(SOCP)は、線形計画法(LP)を二次(ローレンツまたはアイスクリーム)錐で拡張した凸最適化です。解の探索領域は、アフィン線形多様体と二次錐のデカルト積の交差です。SOCPは、工学、制御理論、定量的金融から二次計画法やロバスト最適化まで、幅広い応用分野に現れます。その強力な性質により、金融最適化の重要なツールとなっています。内点法(IPM)は、理論的な多項式複雑性と実用的な性能により、SOCP問題を解くための最も一般的なアプローチです。
nAGは、Mark
SOCPは、その柔軟性と多様性により、ポートフォリオ最適化で広く使用されています。様々な種類の制約を持つ多くの問題を扱うことができ、それらは(SOCP)と同等のSOCPに変換できます。詳細についてはを参照してください。この記事の残りの部分では、nAG Library for PythonのSOCPソルバーを使用して、ポートフォリオ最適化のための様々な実用的な制約を持つモデルを構築し解く方法を示します。
データ準備
2018年3月から2019年3月までのDJIAの30銘柄の日次価格を考えます。実際には、平均リターン
# 必要なライブラリをインポート
import pickle as pkl
import numpy as np
import matplotlib.pyplot as plt
# stock_price.plkから株価データを読み込む
# Stock_price: dict = ['close_price': [データ], 'date_index': [データ]]
= stock_price = pkl.load(open('./data/stock_price.pkl', 'rb'))
stock_price = stock_price['close_price']
close_price = stock_price['date_index'] date_index
# データのサイズ、m: 観測数、n: 株式数
= len(date_index)
m = len(close_price) n
# 株価の終値をnumpy配列に抽出する
= np.zeros(shape=(m, n))
data = 0
i for stock in close_price:
= close_price[stock]
data[:,i]
plt.plot(np.arange(m), data[:,i])+= 1
i # 終値をプロットする
'Time (days)')
plt.xlabel('Closing price ($)')
plt.ylabel( plt.show()

各銘柄
# 相対リターン
= np.zeros(shape=(m-1, n))
rel_rtn for j in range(m-1):
= np.divide(data[j+1,:] - data[j,:], data[j,:])
rel_rtn[j,:] # 相対リターンをプロット
for i in range(n):
-1),rel_rtn[:,i])
plt.plot(np.arange(m'Time (days)')
plt.xlabel('Relative return')
plt.ylabel( plt.show()

各株式の平均リターン
# 平均リターン
= np.zeros(n)
r = rel_rtn.sum(axis=0)
r = r / (m-1)
r # 共分散行列
= np.cov(rel_rtn.T) V
古典的な平均分散モデル
効率的フロンティア
ポートフォリオ管理の主要な目標の1つは、特定のリスク測定の下で一定水準のリターンを達成することです。ここでは、nAGライブラリを使用して、ロングオンリー制約(つまり、買い持ちのみで空売りは許可されない)を持つ古典的なマーコビッツモデルを解くことで、効率的フロンティアを構築する方法を示します:
# nAGライブラリをインポートする
from naginterfaces.base import utils
from naginterfaces.library import opt
from naginterfaces.library import lapackeig
# 必要な数学ライブラリをインポート
import math as mt
import warnings as wn
問題(MV_model)をSOCPを通じて解くには、それを標準形式(SOCP)に変換し、nAG
SOCPソルバーにデータ
ソルバーが必要とするデータを管理するために、辞書構造を作成し維持することができます。
def model_init(n):
"""
Initialize a dict to store the data that is used to feed nAG socp solver
"""
= {
model # 変数の数
'n': n,
# 制約条件の数
'm': 0,
# 目的関数の係数
'c': np.full(n, 0.0, float),
# 変数に対する境界制約
'blx': np.full(n, -1.e20, float),
'bux': np.full(n, 1.e20, float),
# 線形制約の係数とその境界
'linear': {'bl': np.empty(0, float),
'bu': np.empty(0, float),
'irowa': np.empty(0, int),
'icola': np.empty(0, int),
'a': np.empty(0, float)},
# 円錐制約タイプと変数グループ
'cone' : {'type': [],
'group': []}
}return model
データモデルが完成したら、以下の関数を使用してnAG SOCP ソルバーにデータを供給することができます。
def set_nag(model):
"""
Use data in model to feed nAG optimization suite to define problm
"""
# 問題ハンドルを作成する
= opt.handle_init(model['n'])
handle
# 目的関数を設定する
'c'])
opt.handle_set_linobj(handle, model[
# 箱型制約を設定する
'blx'], model['bux'])
opt.handle_set_simplebounds(handle, model[
# 線形制約を設定する
'linear']['bl'], model['linear']['bu'],
opt.handle_set_linconstr(handle, model['linear']['irowa'], model['linear']['icola'], model['linear']['a'])
model[
# 円錐制約を設定する
= 0
i while i<len(model['cone']['type']):
'cone']['type'][i],
opt.handle_set_group(handle,model[0, model['cone']['group'][i])
+= 1
i
# オプションを設定
for option in [
'Print Options = NO',
'Print Level = 1',
'Print File = -1',
'SOCP Scaling = A'
]:
opt.handle_opt_set(handle, option)
return handle
それでは、データをモデルに準備する方法に焦点を当てましょう。二次目的関数をモデルに追加するために、以下の変換が必要です。変数
def factorize(V):
"""
For any given positive semidefinite matrix V, factorize it V = F'*F where
F is kxn matrix that is returned
"""
# Vのサイズ
= V.shape[0]
n
# 注意: Vがスパース行列として入力される場合、スパース因子分解を使用することができます
= lapackeig.dsyevd('V', 'L', V)
U, lamda
# 正の固有値と対応する固有ベクトルを求める
= 0
i = 0
k = []
F
while i<len(lamda):
if lamda[i] > 0:
= np.append(F, mt.sqrt(lamda[i])*U[:,i])
F += 1
k += 1
i
= F.reshape((k, n))
F
return F
以下のコードは一般的な二次目的関数
def add_qobj(model, F, q=None):
"""
Add quadratic objective defined as: 1/2 * x'Vx + q'x
transformed to second order cone by adding artificial variables
Parameters
----------
model: a dict with structure:
{
'n': int,
'm': int,
'c': float numpy array,
'blx': float numpy array,
'bux': float numpy array,
'linear': {'bl': float numpy array,
'bu': float numpy array,
'irowa': int numpy array,
'icola': int numpy array,
'a': float numpy array},
'cone' : {'type': character list,
'group': nested list of int numpy arrays}
}
F: float 2d numpy array
kxn dense matrix that V = F'*F where k is the rank of V
q: float 1d numpy array
n vector
Returns
-------
model: modified structure of model
Note: imput will not be checked
"""
# Fの次元を取得する (kxn)
= F.shape
k, n
# デフォルト q
if q is None:
= np.zeros(n)
q
# 最新の問題サイズ
= model['m']
m_up = model['n']
n_up
# その後、k + 2個のさらなる変数を以下と共に加える必要があります
# k + 1個の線形制約と1つの回転円錐制約
# モデルを拡大する
'n'] = model['n'] + k + 2
model['m'] = model['m'] + k + 1
model[
# 目的関数内のcを初期化する
# 変数の順序は [x, t, y, s] です
'c'][0:n] = q
model['c'] = np.append(model['c'], np.zeros(k+2))
model['c'][n_up] = 1.0
model[
# xの境界を拡大し、新たに追加されたk+2個の変数に無限大の境界を追加する
'blx'] = np.append(model['blx'], np.full(k+2, -1.e20, dtype=float))
model['bux'] = np.append(model['bux'], np.full(k+2, 1.e20, dtype=float))
model[
# 線形制約の拡大
# Fの疎パターンを取得する
= np.nonzero(F)
row, col = F[row, col]
val
# 1ベースに変換し、行を m だけ下に移動する
= row + 1 + m_up
row = col + 1
col # yとtとsの係数を既存の線形係数Aに追加する
# 結果は
# [A, 0, 0, 0;
# F, 0, -I, 0;
# 0, 0, 0, 1]
= np.append(row, np.arange(m_up+1, m_up+k+1+1))
row = np.append(col, np.arange(n_up+2, n_up+k+1+1+1))
col = np.append(val, np.append(np.full(k, -1.0, dtype=float), 1.0))
val 'linear']['irowa'] = np.append(model['linear']['irowa'], row)
model['linear']['icola'] = np.append(model['linear']['icola'], col)
model['linear']['a'] = np.append(model['linear']['a'], val)
model[
'linear']['bl'] = np.append(model['linear']['bl'],
model[1.0))
np.append(np.zeros(k), 'linear']['bu'] = np.append(model['linear']['bu'],
model[1.0))
np.append(np.zeros(k),
# 円錐制約の拡大
'cone']['type'].extend('R')
model[= np.zeros(k+2, dtype=int)
group 0] = n_up + 1
group[1] = n_up + 1 + k + 1
group[2:] = np.arange(n_up+2, n_up+k+1+1)
group['cone']['group'].append(group)
model[
return model
(MV_model)の目的関数が追加されたら、以下の関数を使用して長期のみの制約を追加することができます
def add_longonlycon(model, n, b=None):
"""
Add long-only constraint to model
If no b (benchmark) presents, add sum(x) = 1, x >= 0
If b presents, add sum(x) = 0, x + b >= 0
"""
# 最新の問題サイズ
= model['m']
m
# 制約条件の数が1増加しました
'm'] = model['m'] + 1
model[
# 境界制約: x >=0 または x >= -b
if b is not None:
'blx'][0:n] = -b
model[else:
'blx'][0:n] = np.zeros(n)
model[
# 線形制約: e'x = 1 または e'x = 0
if b is not None:
'linear']['bl'] = np.append(model['linear']['bl'],
model[1, 0.0, dtype=float))
np.full('linear']['bu'] = np.append(model['linear']['bu'],
model[1, 0.0, dtype=float))
np.full(else:
'linear']['bl'] = np.append(model['linear']['bl'],
model[1, 1.0, dtype=float))
np.full('linear']['bu'] = np.append(model['linear']['bu'],
model[1, 1.0, dtype=float))
np.full(
'linear']['irowa'] = np.append(model['linear']['irowa'],
model[+1, dtype=int))
np.full(n, m'linear']['icola'] = np.append(model['linear']['icola'],
model[1, n+1))
np.arange('linear']['a'] = np.append(model['linear']['a'],
model[1.0, dtype=float))
np.full(n,
return model
上記の関数を使用することで、以下のように効率的フロンティアを簡単に構築できます。
def ef_lo_basic(n, r, V, step=None):
"""
Basic model to build efficient frontier with long-only constraint
by solving:
min -r'*x + mu * x'Vx
s.t. e'x = 1, x >= 0
Parameters
----------
n: number of assets
r: expected return
V: covariance matrix
step: define smoothness of the curve of efficient frontier,
mu would be generated from [0, 2000] with step in default
Output:
-------
risk: a vector of risk sqrt(x'Vx) with repect to certain mu
rtn: a vector of expected return r'x with repect to certain mu
"""
# オプション引数を設定する
if step is None:
= 2001
step
# 一度だけVを因数分解し、残りのコードでその因数分解を使用する
# 密行列 V = U*Diag(lamda)*U' の固有値分解を行い、V = F'*F を得る
= factorize(V)
F
= np.empty(0, float)
risk = np.empty(0, float)
rtn
for mu in np.linspace(0.0, 2000.0, step):
# モデルを構築するためのデータ構造を初期化する
= model_init(n)
model
# 2次目的関数
= F * mt.sqrt(2.0*mu)
muf
= add_qobj(model, muf, -r)
model
# 売り持ちなし制約を追加
= add_longonlycon(model, n)
model
# モデルを使用してnAG socpソルバーに供給します
= set_nag(model)
handle
# 内点法SOCPソルバーを呼び出す
# 警告をミュートし、警告からの結果をカウントしない
'error', utils.NagAlgorithmicWarning)
wn.simplefilter(try:
= opt.handle_solve_socp_ipm(handle)
slt
# ポートフォリオのリスクとリターンを計算する
= np.append(risk, mt.sqrt(slt.x[0:n].dot(V.dot(slt.x[0:n]))))
risk = np.append(rtn, r.dot(slt.x[0:n]))
rtn except utils.NagAlgorithmicWarning:
pass
# ハンドルを破棄する:
opt.handle_free(handle)
return risk, rtn
# 効率的フロンティアを構築し、結果をプロットする
= ef_lo_basic(n, r, V, 500)
ab_risk, ab_rtn *100.0, ab_rtn*100.0)
plt.plot(ab_risk'Total Expected Return (%)')
plt.ylabel('Absolute Risk (%)')
plt.xlabel( plt.show()

シャープレシオの最大化
シャープレシオは、ポートフォリオのリターンとポートフォリオの超過リターンの標準偏差の比率として定義されます。通常、ポートフォリオの効率性を測定するために使用されます。最も効率的なポートフォリオを見つけることは、以下の最適化問題を解くことと同等です。
def add_sr_lincon(model, r, n):
"""
Add linear constraints for Sharpe ratio problem
e'y = lamda, y >= 0, r'y = 1, lamda >= 0
Enlarge model by 1 more variable lamda
Return: model and index of lambda in the final result, need it to
reconstruct the original solution
"""
# 最新の問題サイズ
= model['m']
m_up = model['n']
n_up
# さらに1つの変数と2つの線形制約を追加する
'n'] = model['n'] + 1
model['m'] = model['m'] + 2
model[
# パラメータ0.0でcを拡大する
'c'] = np.append(model['c'], 0.0)
model[
# yの境界制約
'blx'][0:n] = np.zeros(n)
model[
# ラムダに境界制約を設定する
'blx'] = np.append(model['blx'], 0.0)
model['bux'] = np.append(model['bux'], 1.e20)
model[
# e'y = lamda を追加
= np.full(n+1, m_up+1, dtype=int)
row = np.append(np.arange(1, n+1), n_up+1)
col = np.append(np.full(n, 1.0, dtype=float), -1.0)
val
# y = 1 を追加
= np.append(row, np.full(n, m_up+2, dtype=int))
row = np.append(col, np.arange(1, n+1))
col = np.append(val, r)
val
# モデルを更新
'linear']['irowa'] = np.append(model['linear']['irowa'], row)
model['linear']['icola'] = np.append(model['linear']['icola'], col)
model['linear']['a'] = np.append(model['linear']['a'], val)
model[
# 線形制約の境界
'linear']['bl'] = np.append(model['linear']['bl'],
model[1), 1.0))
np.append(np.zeros('linear']['bu'] = np.append(model['linear']['bu'],
model[1), 1.0))
np.append(np.zeros(
return model, n_up
これで以下のようにnAG SOCPソルバーを呼び出すことができます。
def sr_lo_basic(n, r, V):
"""
Basic model to calculate efficient portfolio that maximize the Sharpe ratio
min y'Vy
s.t. e'y = lamda, y >= 0, r'y = 1, lamda >= 0
Return efficient portfolio y/lamda and corresponding risk and return
"""
# 一度だけVを因数分解し、残りのコードでその因数分解を使用する
# 密行列 V = U*Diag(lamda)*U' の固有値分解を行い、V = F'*F を得る
= factorize(V)
F
# モデルを構築するためのデータ構造を初期化する
= model_init(n)
model
# 2次目的関数
= F * mt.sqrt(2.0)
muf
= add_qobj(model, muf)
model
# 線形制約を追加する
= add_sr_lincon(model, r, n)
model, lamda_idx
# モデルを使用してnAG socpソルバーに供給します
= set_nag(model)
handle
# 内点法SOCPソルバーを呼び出す
= opt.handle_solve_socp_ipm(handle)
slt
= mt.sqrt(slt.x[0:n].dot(V.dot(slt.x[0:n])))/slt.x[lamda_idx]
sr_risk = r.dot(slt.x[0:n])/slt.x[lamda_idx]
sr_rtn
return sr_risk, sr_rtn, slt.x[0:n]/slt.x[lamda_idx]
# 最も効率的なポートフォリオを計算し、結果をプロットします。
= sr_lo_basic(n, r, V)
sr_risk, sr_rtn, sr_x *100.0, ab_rtn*100.0, label='Efficient frontier')
plt.plot(ab_risk*100], [sr_rtn*100], 'rs',
plt.plot([sr_risk='Portfolio with maximum Sharpe ratio')
label*100, 0.0], [sr_rtn*100, 0.0], 'r-', label='Capital market line')
plt.plot([sr_riskmin(ab_risk*100), max(ab_risk*100), min(ab_rtn*100), max(ab_rtn*100)])
plt.axis(['Total Expected Return (%)')
plt.ylabel('Absolute Risk (%)')
plt.xlabel(
plt.legend() plt.show()

トラッキングエラー制約付きポートフォリオ最適化
ベンチマークを上回る際に不必要なリスクを取ることを避けるため、投資家は一般的にアクティブポートフォリオのベンチマークからの乖離の変動性に制限を設けます。これはトラッキングエラー変動性(TEV)としても知られています
。超過リターン空間で効率的フロンティアを構築するモデルは以下の通りです:
# 効率的ポートフォリオからベンチマークポートフォリオを生成する
# シャープレシオを最大化する
# xを摂動させる
= sr_x + 1.e-1
b # bを正規化する
= b/sum(b)
b
# ベンチマークにおけるリスクとリターンを計算する
= mt.sqrt(b.dot(V.dot(b)))
b_risk = r.dot(b) b_rtn
問題(MV_model)と同様に、(tev_model)の目的関数は二次式であるため、
def add_qcon(model, F, q=None, r=None):
"""
Add quadratic contraint defined as: 1/2 * x'Vx + q'x + r <= 0,
which is equivalent to t + q'x + r = 0, 1/2 * x'Vx <= t,
transformed to second order cone by adding artificial variables
Parameters
----------
model: a dict with structure:
{
'n': int,
'm': int,
'c': float numpy array,
'blx': float numpy array,
'bux': float numpy array,
'linear': {'bl': float numpy array,
'bu': float numpy array,
'irowa': int numpy array,
'icola': int numpy array,
'a': float numpy array},
'cone' : {'type': character list,
'group': nested list of int numpy arrays}
}
F: float 2d numpy array
kxn dense matrix that V = F'*F where k is the rank of V
q: float 1d numpy array
n vector
r: float scalar
Returns
-------
model: modified structure of model
Note: imput will not be checked
"""
# デフォルトパラメータ
if r is None:
= 0.0
r
# Fの次元を取得する (kxn)
= F.shape
k, n
# 最新の問題サイズ
= model['m']
m_up = model['n']
n_up
# その後、k + 2個のさらなる変数を以下と共に加える必要があります
# k + 2個の線形制約と1つの回転円錐制約
# モデルを拡大する
'n'] = model['n'] + k + 2
model['m'] = model['m'] + k + 2
model[
# 追加されたすべての補助変数は目的関数に関与しない
# したがって、目的関数における彼らの係数はすべてゼロです。
'c'] = np.append(model['c'], np.zeros(k+2))
model[
# xの境界を拡大し、新たに追加されたk+2個の変数に無限大の境界を追加する
'blx'] = np.append(model['blx'], np.full(k+2, -1.e20, dtype=float))
model['bux'] = np.append(model['bux'], np.full(k+2, 1.e20, dtype=float))
model[
# 線形制約の拡大
= np.nonzero(F)
row, col = F[row, col]
val
# 1ベースに変換し、行を m_up 分下に移動する
# Fx = y と s = 1 を加える
# [x,t,y,s]
= row + 1 + m_up
row = col + 1
col = np.append(np.append(row, np.arange(m_up+1, m_up+k+1+1)), m_up+k+1+1)
row = np.append(np.append(col, np.arange(n_up+2, n_up+k+1+1+1)), n_up+1)
col = np.append(np.append(val, np.append(np.full(k, -1.0,
val =float), 1.0)), 1.0)
dtype'linear']['irowa'] = np.append(model['linear']['irowa'], row)
model['linear']['icola'] = np.append(model['linear']['icola'], col)
model['linear']['a'] = np.append(model['linear']['a'], val)
model[
'linear']['bl'] = np.append(np.append(model['linear']['bl'],
model[1.0)), -r)
np.append(np.zeros(k), 'linear']['bu'] = np.append(np.append(model['linear']['bu'],
model[1.0)), -r)
np.append(np.zeros(k),
# t + q'x + r = 0 を加える
if q is not None:
'linear']['irowa'] = np.append(model['linear']['irowa'],
model[+k+2, dtype=int))
np.full(n, m_up'linear']['icola'] = np.append(model['linear']['icola'],
model[1, n+1))
np.arange('linear']['a'] = np.append(model['linear']['a'], q)
model[
# 円錐制約の拡大
'cone']['type'].extend('R')
model[= np.zeros(k+2, dtype=int)
group 0] = n_up + 1
group[1] = n_up + 1 + k + 1
group[2:] = np.arange(n_up+2, n_up+k+1+1)
group['cone']['group'].append(group)
model[
return model
上記の関数を使用することで、以下のようにTEV制約付きの効率的フロンティアを簡単に構築できます。
def tev_lo(n, r, V, b, tev, step=None):
"""
TEV contrained portforlio optimization with absolute risk taken into
consideration by solving:
min -r'y + mu*(b+y)'V(b+y)
s.t. sum(y) = 0, y+b >=o, y'Vy <= tev
"""
# オプション引数の設定
if step is None:
= 2001
step
# 一度だけVを因数分解し、残りのコードでその因数分解を使用する
# 密行列 V = U*Diag(lamda)*U' の固有値分解を行い、V = F'*F を得る
= factorize(V)
F
= np.empty(0, float)
risk = np.empty(0, float)
rtn
for mu in np.linspace(0.0, 2000.0, step):
# モデルを構築するためのデータ構造を初期化する
= model_init(n)
model
# 売り持ちなし制約を追加
= add_longonlycon(model, n, b)
model
# 2次目的関数
= F * mt.sqrt(2.0*mu)
muf = 2.0*mu*V.dot(b) - r
mur
= add_qobj(model, muf, mur)
model
# 二次制約 y'Vy <= tev を追加
= F * mt.sqrt(2.0)
F_hf = add_qcon(model, F_hf, r=-tev)
model
# モデルを使用してnAG socpソルバーに供給します
= set_nag(model)
handle
# 内点法SOCPソルバーを呼び出す
# 警告を無効にし、警告からの結果をカウントしない
'error', utils.NagAlgorithmicWarning)
wn.simplefilter(try:
= opt.handle_solve_socp_ipm(handle)
slt
# ポートフォリオのリスクとリターンを計算する
= np.append(risk, mt.sqrt((slt.x[0:n]+b).dot(V.dot(slt.x[0:n]+b))))
risk = np.append(rtn, r.dot(slt.x[0:n]+b))
rtn except utils.NagAlgorithmicWarning:
pass
# ハンドルを破棄する:
opt.handle_free(handle)
return risk, rtn
# トラッキングエラーの制限を設定する
= 0.000002
tev # モデルを解く
= tev_lo(n, r, V, b, tev, step=500)
tev_risk, tev_rtn # 結果をプロットする
=(7.5, 5.5))
plt.figure(figsize*100.0, ab_rtn*100.0, label='Classic efficient frontier')
plt.plot(ab_risk*100], [sr_rtn*100], 'rs',
plt.plot([sr_risk='Portfolio with maximum Sharpe ratio')
label*100, 0.0], [sr_rtn*100, 0.0], 'r-', label='Capital market line')
plt.plot([sr_risk*100, b_rtn*100, 'r*', label='Benchmark portfolio')
plt.plot(b_risk*100.0, tev_rtn*100.0, 'seagreen',
plt.plot(tev_risk='Efficient frontier with tev constraint')
label
min(ab_risk*100), max(ab_risk*100), min(tev_rtn*100), max(ab_rtn*100)])
plt.axis(['Total Expected Return (%)')
plt.ylabel('Absolute Risk (%)')
plt.xlabel(
plt.legend() plt.show()

結論
このノートブックでは、ポートフォリオ最適化におけるさまざまなモデルを解くためにnAGライブラリを使用する方法を示しました。上記で言及した関数のいくつかを使用して、すぐに独自のモデルを構築し始めることができます。SOCPの汎用性は、ここで言及したモデルだけに限定されないことを指摘する価値があります。それはさらに多くの問題と制約をカバーしています。例えば、DeMiguelらはノルム制約付きのポートフォリオ最適化について議論しており、これは容易にSOCP問題に変換できます。SOCPソルバーに関するnAGライブラリのドキュメントとを参照してください。
参考文献
[1] Alizadeh Farid and Goldfarb Donald, Second-order cone programming’’, Mathematical programming, vol. 95, number 1, pp. 3–51, 2003.
[2] Lobo Miguel Sousa, Vandenberghe Lieven, Boyd Stephen et al., Applications of second-order cone programming’’, Linear algebra and its applications, vol. 284, number 1-3, pp. 193–228, 1998.
[3] Jorion Philippe, Portfolio optimization with tracking-error constraints’’, Financial Analysts Journal, vol. 59, number 5, pp. 70–82, 2003.
[4] Roll Richard, A mean/variance analysis of tracking error’’, The Journal of Portfolio Management, vol. 18, number 4, pp. 13–22, 1992.
[5] DeMiguel Victor, Garlappi Lorenzo, Nogales Francisco J et al., A generalized approach to portfolio optimization: Improving performance by constraining portfolio norms’’, Management science, vol. 55, number 5, pp. 798–812, 2009.
[6] Numerical Algorithms Group, nAG documentation’’, 2019. online