σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyロードマップ
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップ
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python ロードマップ›モジュール 2 · 市場データのためのNumPyとpandas›トピック 2.11·~35 分中級

対数リターン

ロードマップ上の位置モジュール 2/11 · トピック 11/20
2.11 対数リターン
—ラボスコア— / 100合計XP—連続日数—

価格比の対数

対数リターンは2つの価格の比の自然対数です。P_t / P_{t-1} - 1 の代わりに ln(P_t / P_{t-1}) を計算します。小さな変動ではほぼ同じ値になりますが(2%の上昇は対数で0.0198)、対数版には統計やモデリングの標準になっている性質があります。pandas では価格の Series 全体に np.log をかけてから diff を取ります。対数の引き算は価格の割り算だからです。

r_t = ln(P_t / P_{t-1}) = ln(P_t) - ln(P_{t-1})
import numpy as np
import pandas as pd

prices = pd.Series([100.0, 102.0, 100.98, 103.0], index=["Mon", "Tue", "Wed", "Thu"])

log_ret = np.log(prices).diff()
print(log_ret.round(4))
# Mon       NaN
# Tue    0.0198
# Wed   -0.0101
# Thu    0.0198
# dtype: float64

print(prices.pct_change().round(4).tolist())   # [nan, 0.02, -0.01, 0.02]   the simple returns, for comparison

対数リターンは時間方向に足し算できる

これが使う最大の理由です。ln(a) - ln(c) = (ln(a) - ln(b)) + (ln(b) - ln(c)) なので、複数期間の対数リターンは期間ごとの対数リターンの合計にすぎず、複利計算は不要です。2日間のリターンは日次2つの和、月次は日次の和で、exp(total) - 1 で単純リターンに戻せます。対数リターンは対称でもあります。価格が2倍になって半分に戻ると、対数リターンは +0.6931 と -0.6931 で打ち消し合いますが、単純リターンは +100% と -50% でした。

# The sum of the daily log returns is the log return over the whole period
print(round(log_ret.sum(), 6))                         # 0.029559
print(round(np.log(prices.iloc[-1] / prices.iloc[0]), 6))   # 0.029559   end price over start price

# exp(total) - 1 is the simple return of the whole period
print(round(np.exp(log_ret.sum()) - 1, 6))             # 0.03
# Symmetry: the price doubles, then halves
doubling = pd.Series([100.0, 200.0, 100.0])
print(doubling.pct_change().tolist())               # [nan, 1.0, -0.5]
print(np.log(doubling).diff().round(4).tolist())    # [nan, 0.6931, -0.6931]

変換と複数日の値動き

単純リターンに戻すには np.exp(r) - 1 を使います(極めて小さな値では np.expm1(r) のほうが正確です)。小さな変動ではよく一致し、大きな変動では差が開きます。単純リターン +50% は対数で 0.4055 です。np.log と np.exp は Series 全体に働くのでループは要りません。複数日の値動きを測るには、diff に期間数を渡します。np.log(prices).diff(5) は5日間の対数リターンで、日次の対数リターン5つの合計に等しくなります。

# Small moves agree with the simple return, big moves do not
for simple in [0.01, 0.1, 0.5, -0.5]:
    print(simple, round(np.log1p(simple), 4))   # log1p(r) is ln(1 + r)
# 0.01 0.01
# 0.1 0.0953
# 0.5 0.4055
# -0.5 -0.6931

# Back to simple returns, on the whole Series at once
print((np.exp(log_ret) - 1).round(4).tolist())   # [nan, 0.02, -0.01, 0.02]

# The log return over 2 days: diff takes a number of periods
print(np.log(prices).diff(2).round(4).tolist())   # [nan, nan, 0.0098, 0.0098]
足すのは時間方向であって、資産方向ではない対数リターンは期間をまたいで足せますが、ポートフォリオのリターンは各資産の単純リターンの加重和です。同額の2つの保有の対数リターンが +10% と -10% でも、ポートフォリオは 0% になりません。単純リターンでは +10.5% と -9.5% なので、ポートフォリオは +0.5% です。先に単純リターンへ変換して加重し、必要なら対数に戻しましょう。
  • 対数リターンは ln(価格 / 前の価格):np.log(prices).diff()。最初の要素は NaN
  • 時間方向では単純に足せる:log_ret.sum()。np.exp(total) - 1 で単純リターンになる
  • diff(k) は k 期間の値動きを返す。資産間の加重は対数ではなく単純リターンで行う

次は DatetimeIndex と時間によるインデックスです。価格の系列に本物の日付を持たせ、月や曜日で切り出します。

ラボ:採点される内容

  • log_returns で価格を対数リターンに変換する25点
  • simple_from_log で対数リターンを単純リターンに戻す25点
  • total_log_return で期間ごとの対数リターンを合計する25点
  • multi_day_log_return で複数日の値動きを測る25点
エクスプローラー · lab-44-1
▾ lab-44-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-44-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-44-1 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ

コードを実行するにはログインしてください。

レクチャーはすべて無料で読めます。コードの実行、テスト、ラボの提出にはアカウントの作成かログインが必要です。

  • ブラウザ内IDEとbashターミナル。インストール不要
  • 即時採点、XP、ロードマップでの現在地
アカウントを作成ログイン
または
GitHubで続ける