diff --git a/src/OnlinePortfolioAnalytics.jl b/src/OnlinePortfolioAnalytics.jl index 11e67a6..b5d16f4 100644 --- a/src/OnlinePortfolioAnalytics.jl +++ b/src/OnlinePortfolioAnalytics.jl @@ -13,6 +13,7 @@ export CumulativeReturn export DrawDowns, ArithmeticDrawDowns export AssetReturnMoments export Sharpe +export Sortino export fit!, value @@ -25,6 +26,7 @@ include("std_dev.jl") include("drawdowns.jl") include("moments.jl") include("sharpe.jl") +include("sortino.jl") include("value_at_risk.jl") diff --git a/src/sortino.jl b/src/sortino.jl new file mode 100644 index 0000000..41ae49d --- /dev/null +++ b/src/sortino.jl @@ -0,0 +1,26 @@ +mutable struct Sortino{T} <: PortfolioAnalytics{T} + value::T + n::Int + + mean_ret::Mean + stddev_neg_ret::StdDev + + period::Int + risk_free::T + + function Sortino{T}(; period=252, risk_free=0) where {T} + new{T}(T(0), 0, Mean(), StdDev{T}(), period, risk_free) + end +end + +function OnlineStatsBase._fit!(stat::Sortino, ret) + fit!(stat.mean_ret, ret) + if ret < 0 + fit!(stat.stddev_neg_ret, ret) + end + stat.n += 1 + mean_return = value(stat.mean_ret) + stddev_neg_return = value(stat.stddev_neg_ret) + sortino = sqrt(stat.period) * (mean_return - stat.risk_free) / stddev_neg_return + stat.value = sortino +end diff --git a/test/runtests.jl b/test/runtests.jl index f159fe2..fce80fb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -362,8 +362,28 @@ const weights = [0.4, 0.4, 0.2] push!(sharpes, value) end subscribe!(mapped_source, observer) - + @test isapprox(sharpes[end], 0.2886, atol = ATOL) end + @testset "Sortino" begin + source = from(TSLA) + _ret = SimpleAssetReturn{Float64}() + _sortino = Sortino{Float64}() + + mapped_source = + source |> + map(Union{Missing,Float64}, price -> (fit!(_ret, price); + value(_ret))) |> + filter(!ismissing) |> + map(Any, r -> (fit!(_sortino, r); value(_sortino))) + + sortinos = Float64[] + function observer(value) + push!(sortinos, value) + end + subscribe!(mapped_source, observer) + + @test isapprox(sortinos[end], 11.4992, atol = ATOL) + end end