最適化とは関数にルールを足したもの
scipy.optimize.minimize(fun, x0, method='SLSQP', bounds=..., constraints=...) は fun を最小にするベクトルを探します。与える情報は3種類です。目的関数 fun(w) は最小化する数を返します。最小分散ポートフォリオなら w @ cov @ w で、何かを最大化したいときはその符号を反転して最小化します。境界は重みごとの (下限, 上限) のリストで、(0, cap) は空売りなし・上限超えのポジションなしを意味します。制約は辞書で、{'type': 'eq', 'fun': lambda w: w.sum() - 1} は重みの合計を1にします(eq の関数は0に等しく、ineq の関数は0以上でなければなりません)。出発点 x0 も重要で、等ウェイトは妥当で実行可能な出発点です。
2つの細部が答えの正しさを決めます。第一にスケールです。日次の分散は0.0001のような数で、SLSQPは目的関数の変化が許容誤差 ftol(既定1e-6)を下回ると止まるため、日次の生の数字だと出発点から動かずに止まることがあります。入力を年率換算(×252)し、options={'ftol': 1e-12, 'maxiter': 500} を渡し、必ず result.success と重みがルールを守っているかを確認します。第二に定式化です。シャープレシオの最大化は比なので -(w @ mu) / sqrt(w @ cov @ w) を最小化し、リスクパリティは等しいリスク割合 wᵢ(Σw)ᵢ/ wᵀΣw を求めるので 1/n との差の二乗和を最小化し、効率的フロンティアは目標リターンごとの最小分散問題で、w @ mu - target という2つ目の eq 制約を加えます。
- スケールと許容誤差:入力を年率換算し
ftolを厳しくしないと、SLSQPは出発点で止まることがある。 - 答えを確かめる:重みの合計は1で境界を守ること。
result.successを見る。 - 1つの道具、4つの問題:最小分散、負のシャープレシオ、等リスク割合、目標リターンごとの問題は、目的関数と制約が違うだけの
minimize呼び出し。
ラボ:採点される内容
- 年率の入力8点
- ポートフォリオのリターンとリスク8点
- 上限付き最小分散18点
- シャープレシオの最大化18点
- リスク寄与10点
- リスクパリティ16点
- フロンティアを一点ずつ10点
- 3つのポートフォリオ12点