From 50ebc334f9239dc499ef435c078d121990e5b14a Mon Sep 17 00:00:00 2001 From: alecloudenback Date: Wed, 27 Dec 2023 19:27:19 -0600 Subject: [PATCH] fixup Equity & Option Module & tests, add EuroPut (#172) --- src/Contract.jl | 21 ++++++++++++++++++++- src/model/Equity.jl | 27 ++++++++++++++++++++++----- test/Equity.jl | 41 +++++++++++++++++++++++++++++------------ test/runtests.jl | 1 + 4 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/Contract.jl b/src/Contract.jl index ffad590f..332037a7 100644 --- a/src/Contract.jl +++ b/src/Contract.jl @@ -358,11 +358,30 @@ A European call option on the given contract with the given strike and maturity. EuroCall{S,K,M} <: FinanceCore.AbstractContract <: Any +""" +struct EuroCall{S<:AbstractContract,K<:Real,M<:Timepoint} <: AbstractContract + underlying::S + strike::K + maturity::M +end +""" + EuroPut(contract,strike,maturity) +A European put option on the given contract with the given strike and maturity. + +# Arguments + - contract::AbstractContract - The underlying contract. + - strike::Real - The strike price. + - maturity::Union{Real,Date} - The maturity of the option. + + Supertype Hierarchy +≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡ + + EuroPut{S,K,M} <: FinanceCore.AbstractContract <: Any """ -struct EuroCall{S<:AbstractContract,K<:Real,M<:Timepoint} <: AbstractContract +struct EuroPut{S<:AbstractContract,K<:Real,M<:Timepoint} <: AbstractContract underlying::S strike::K maturity::M diff --git a/src/model/Equity.jl b/src/model/Equity.jl index 736e2f68..005c6045 100644 --- a/src/model/Equity.jl +++ b/src/model/Equity.jl @@ -4,7 +4,13 @@ The `Equity` module provides equity-related model definitions. See also: the [`Volatility`](@ref FinanceModels.Volatility-API-Reference) module. """ module Equity + import ..AbstractModel +import ..Volatility +import ..Option +import ..CommonEquity +import ..eurocall, ..europut +using ..FinanceCore abstract type AbstractEquityModel <: AbstractModel end @@ -14,8 +20,8 @@ abstract type AbstractEquityModel <: AbstractModel end A struct representing the Black-Scholes-Merton model for equity prices. # Arguments -- `r`: The risk-free rate. -- `q`: The dividend yield. +- `r`: The risk-free rate (continuously compounded scalar or a `FinanceCore.Rate` type). +- `q`: The dividend yield (continuously compounded scalar or a `FinanceCore.Rate` type). - `σ`: The volatility model of the underlying asset (see [`Volatility`](@ref FinanceModels.Volatility-API-Reference) module) # Fields @@ -57,14 +63,20 @@ struct BlackScholesMerton{T,U,V} <: AbstractEquityModel r::T # risk free rate q::U # dividend yield σ::V # roughly equivalent to the volatility in the usual lognormal model multiplied by F^{1-β}_{0} -end + # an inner constructor: + + function BlackScholesMerton(r, q, σ::V) where {V} + rc = rate(Continuous(r)) + qc = rate(Continuous(q)) + new{typeof(rc),typeof(qc),V}(rc, qc, σ) + end end """ - volatility(volatiltiy_model,strike_ratio,time_to_maturity) + volatility(volatility_model,strike_ratio,time_to_maturity) -Returns the volatility associated with the moneyness (strike/price ratio) and time to maturity. +Returns the volatility associated with the money-ness (strike/price ratio) and time to maturity. """ function volatility(vol::Volatility.Constant, strike_ratio, time_to_maturity) return vol.σ @@ -72,5 +84,10 @@ end function FinanceCore.present_value(model::M, c::Option.EuroCall{CommonEquity,K,T}) where {M<:Equity.BlackScholesMerton,K,T} eurocall(; S=1.0, K=c.strike, τ=c.maturity, r=model.r, q=model.q, σ=model.σ) +end + +function FinanceCore.present_value(model::M, c::Option.EuroPut{CommonEquity,K,T}) where {M<:Equity.BlackScholesMerton,K,T} + europut(; S=1.0, K=c.strike, τ=c.maturity, r=model.r, q=model.q, σ=model.σ) +end end \ No newline at end of file diff --git a/test/Equity.jl b/test/Equity.jl index 66fc1011..48f448b8 100644 --- a/test/Equity.jl +++ b/test/Equity.jl @@ -4,28 +4,28 @@ # tested against https://option-price.com/index.php params = (S=1.0, K=1.0, τ=1, r=0.05, σ=0.25, q=0.0) - @test eurocall(; params...) ≈ 0.12336 atol = 1e-5 - @test europut(; params...) ≈ 0.07459 atol = 1e-5 + @test FinanceModels.eurocall(; params...) ≈ 0.12336 atol = 1e-5 + @test FinanceModels.europut(; params...) ≈ 0.07459 atol = 1e-5 params = (S=1.0, K=1.0, τ=1, r=0.05, σ=0.25, q=0.03) - @test eurocall(; params...) ≈ 0.105493 atol = 1e-5 - @test europut(; params...) ≈ 0.086277 atol = 1e-5 + @test FinanceModels.eurocall(; params...) ≈ 0.105493 atol = 1e-5 + @test FinanceModels.europut(; params...) ≈ 0.086277 atol = 1e-5 params = (S=1.0, K=0.5, τ=1, r=0.05, σ=0.25, q=0.03) - @test eurocall(; params...) ≈ 0.49494 atol = 1e-5 - @test europut(; params...) ≈ 0.00011 atol = 1e-5 + @test FinanceModels.eurocall(; params...) ≈ 0.49494 atol = 1e-5 + @test FinanceModels.europut(; params...) ≈ 0.00011 atol = 1e-5 params = (S=1.0, K=0.5, τ=1, r=0.05, σ=0.25, q=0.03) - @test eurocall(; params...) ≈ 0.49494 atol = 1e-5 - @test europut(; params...) ≈ 0.00011 atol = 1e-5 + @test FinanceModels.eurocall(; params...) ≈ 0.49494 atol = 1e-5 + @test FinanceModels.europut(; params...) ≈ 0.00011 atol = 1e-5 params = (S=1.0, K=0.5, τ=0, r=0.05, σ=0.25, q=0.03) - @test eurocall(; params...) ≈ 0.5 atol = 1e-5 - @test europut(; params...) ≈ 0.0 atol = 1e-5 + @test FinanceModels.eurocall(; params...) ≈ 0.5 atol = 1e-5 + @test FinanceModels.europut(; params...) ≈ 0.0 atol = 1e-5 params = (S=1.0, K=1.5, τ=0, r=0.05, σ=0.25, q=0.03) - @test eurocall(; params...) ≈ 0.0 atol = 1e-5 - @test europut(; params...) ≈ 0.5 atol = 1e-5 + @test FinanceModels.eurocall(; params...) ≈ 0.0 atol = 1e-5 + @test FinanceModels.europut(; params...) ≈ 0.5 atol = 1e-5 end end @@ -40,6 +40,14 @@ end @test pv(m, a) ≈ 0.05410094201902403 + ap = Option.EuroPut(CommonEquity(), 1.0, 1.0) + + # put call parity with continuous dividend yield + # https://web.ma.utexas.edu/users/mcudina/m339d_lecture6.pdf, pg 2 + @test pv(m, a) - pv(m, ap) ≈ 1.0 * exp(-0.02) - exp(-0.01) * 1.0 + + + # test fitting volatility qs = [ Quote(0.0541, a), Quote(0.072636, b), @@ -47,4 +55,13 @@ end m = Equity.BlackScholesMerton(0.01, 0.02, Volatility.Constant()) fit(m, qs) @test fit(m, qs).σ ≈ 0.15 atol = 1e-4 + + # test constructor with Rates + @test Equity.BlackScholesMerton(Continuous(0.01), Continuous(0.02), 0.15) isa Equity.BlackScholesMerton + m = Equity.BlackScholesMerton(Periodic(0.01, 1), Periodic(0.02, 1), 0.15) + # periodic rates should convert to Continuous equivalent that's higher than the Continuous rate + @test m.r < 0.01 + @test m.q < 0.02 + + end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 241e1598..39487210 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -12,6 +12,7 @@ using Transducers include("generic.jl") include("sp.jl") +include("Equity.jl") include("Yield.jl") include("CompositeYield.jl") include("SmithWilson.jl")