From 10316dcfeb01a0e1ce1f2a8d8280135a3e4c0ab5 Mon Sep 17 00:00:00 2001 From: Shayan Date: Wed, 6 Mar 2024 11:55:23 +0330 Subject: [PATCH 1/5] =?UTF-8?q?[CORN.jl]=20Code=20refacoring=20?= =?UTF-8?q?=F0=9F=94=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Algos/CORN.jl | 85 +++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/src/Algos/CORN.jl b/src/Algos/CORN.jl index 75f16c91..eee67bcc 100644 --- a/src/Algos/CORN.jl +++ b/src/Algos/CORN.jl @@ -1,17 +1,17 @@ """ cornu( - adj_close::Matrix{T}, + x::AbstractMatrix{T}, horizon::M, w::M; rho::T=0.2, init_budg=1, progress::Bool=false - ) where {T<:Float64, M<:Int} + ) where {T<:AbstractFloat, M<:Integer} Run CORN-U algorithm. # Arguments -- `adj_close::Matrix{T}`: Adjusted close prices of assets. +- `x::AbstractMatrix{T}`: price relative matrix of assets. - `horizon::M`: The number of periods to invest. - `w::M`: maximum length of time window to be examined. @@ -21,18 +21,18 @@ Run CORN-U algorithm. - `progress::Bool=false`: Whether to show the progress bar. !!! warning "Beware!" - `adj_close` should be a matrix of size `n_assets` × `n_periods`. + `x` should be a matrix of size `n_assets` × `n_periods`. # Returns -- `::OPSAlgorithm(n_assets, b, alg)`: An object of type `OPSAlgorithm`. +- `::OPSAlgorithm`: An object of type [`OPSAlgorithm`](@ref). # Examples ```julia julia> using OnlinePortfolioSelection -julia> adj_close = rand(5, 100); +julia> x = rand(5, 100); -julia> model = cornu(adj_close, 10, 5, 0.5); +julia> model = cornu(x, 10, 5, 0.5); julia> model.alg "CORN-U" @@ -47,32 +47,27 @@ See [`cornk`](@ref), and [`dricornk`](@ref). > [CORN: Correlation-driven nonparametric learning approach for portfolio selection](https://doi.org/10.1145/1961189.1961193) """ function cornu( - adj_close::Matrix{T}, + x::AbstractMatrix{T}, horizon::M, w::M; rho::T=0.2, init_budg=1, progress::Bool=false -) where {T<:Float64, M<:Int} - +) where {T<:AbstractFloat, M<:Integer} + n_assets, _ = size(x) 0≤rho<1 || ArgumentError("The value of `rho` should be in the range of [0, 1).") |> throw - n_experts = w - - # Calculate relative prices - relative_prices = adj_close[:, 2:end] ./ adj_close[:, 1:end-1] - n_assets = size(relative_prices, 1) - q = 1/w - + n_experts = w + q = 1/w # Store the budgets of experts in each period t Sₜ_ = zeros(T, n_experts, horizon+1) Sₜ_[:, 1] .= init_budg weights = zeros(T, n_assets, horizon) + bₜ = similar(x, n_assets, n_experts) for t ∈ 0:horizon-1 - bₜ = Matrix{T}(undef, n_assets, n_experts) for ω ∈ 1:w - b = corn_expert(relative_prices, horizon, ω, rho, t, n_assets) + b = corn_expert(x, horizon, ω, rho, t, n_assets) bₜ[:, ω] = b - Sₜ_[ω, t+2] = S(Sₜ_[ω, t+1], b, relative_prices[:, end-horizon+t+1]) + Sₜ_[ω, t+2] = S(Sₜ_[ω, t+1], b, x[:, end-horizon+t+1]) end progress && progressbar(stdout, horizon, t+1) weights[:, t+1] = final_weights(q, Sₜ_[:, t+2], bₜ) @@ -83,19 +78,19 @@ end """ cornk( - adj_close::Matrix{Float64}, + x::AbstractMatrix{<:AbstractFloat}, horizon::T, k::T, w::T, p::T; init_budg=1, progress::Bool=false - ) where T<:Int + ) where T<:Integer Run CORN-K algorithm. # Arguments -- `adj_close::Matrix{Float64}`: Adjusted close prices of assets. +- `x::AbstractMatrix{<:AbstractFloat}`: price relative matrix of assets. - `horizon::T`: The number of periods to invest. - `k::T`: The number of top experts to be selected. - `w::T`: maximum length of time window to be examined. @@ -106,7 +101,7 @@ Run CORN-K algorithm. - `progress::Bool=false`: Whether to show the progress bar. !!! warning "Beware!" - `adj_close` should be a matrix of size `n_assets` × `n_periods`. + `x` should be a matrix of size `n_assets` × `n_periods`. # Returns - `::OPSAlgorithm(n_assets, b, alg)`: An object of type `OPSAlgorithm`. @@ -115,9 +110,9 @@ Run CORN-K algorithm. ```julia julia> using OnlinePortfolioSelection -julia> adj_close = rand(5, 100); +julia> x = rand(5, 100); -julia> model = cornk(adj_close, 10, 3, 5, 3); +julia> model = cornk(x, 10, 3, 5, 3); julia> model.alg "CORN-K" @@ -132,38 +127,34 @@ See [`cornu`](@ref), and [`dricornk`](@ref). > [CORN: Correlation-driven nonparametric learning approach for portfolio selection](https://doi.org/10.1145/1961189.1961193) """ function cornk( - adj_close::Matrix{Float64}, + x::AbstractMatrix{<:AbstractFloat}, horizon::T, k::T, w::T, p::T; init_budg=1, progress::Bool=false -) where T<:Int - +) where T<:Integer p<2 && ArgumentError("The value of `p` should be more than 1.") |> throw n_experts = w*(p+1) k>n_experts && ArgumentError( "The value of k ($k) is more than number of experts ($n_experts)" ) |> throw - - # Calculate relative prices - relative_prices = adj_close[:, 2:end] ./ adj_close[:, 1:end-1] - n_assets = size(relative_prices, 1) - P = (iszero(pᵢ) ? 0. : (pᵢ-1)/pᵢ for pᵢ∈0:p) - q = 1/k - weights = zeros(Float64, n_assets, horizon) - Sₜ_ = zeros(Float64, n_experts, horizon+1) - Sₜ_[:, 1] .= init_budg + n_assets = size(x, 1) + P = (iszero(pᵢ) ? 0. : (pᵢ-1)/pᵢ for pᵢ∈0:p) + q = 1/k + weights = similar(x, n_assets, horizon) + Sₜ_ = similar(x, n_experts, horizon+1) + Sₜ_[:, 1] .= init_budg + bₜ = similar(x, n_assets, n_experts) for t ∈ 0:horizon-1 - bₜ = Matrix{Float64}(undef, n_assets, n_experts) expert = 1 for ω ∈ 1:w - for ρ ∈ P - b = corn_expert(relative_prices, horizon, ω, ρ, t, n_assets) - bₜ[:, expert] = b - Sₜ_[expert, t+2] = S( - Sₜ_[expert, t+1], b, relative_prices[:, end-horizon+t+1] + for (idxρ, ρ) ∈ enumerate(P) + b = corn_expert(x, horizon, ω, ρ, t, n_assets) + bₜ[:, ω+idxρ] = b + Sₜ_[ω+idxρ, t+2] = S( + Sₜ_[ω+idxρ, t+1], b, x[:, end-horizon+t+1] ) expert += 1 end @@ -185,7 +176,7 @@ end rho::T, t::S, n_assets::S - ) where {T<:Float64, S<:Int} + ) where {T<:AbstractFloat, S<:Int} Create an expert to perform the algorithm according to the given parameters. @@ -198,7 +189,7 @@ Create an expert to perform the algorithm according to the given parameters. - `n_assets::S`: number of assets. # Returns -- `::Vector{Float64}`: Weights of assets. +- `::Vector{AbstractFloat}`: Weights of assets. """ function corn_expert( relative_prices::Matrix{T}, @@ -207,7 +198,7 @@ function corn_expert( rho::T, t::S, n_assets::S -) where {T<:Float64, S<:Int} +) where {T<:AbstractFloat, S<:Int} horizon≥size(relative_prices, 2) && ArgumentError("""The "horizon" ($horizon) is \ bigger than data samples $(size(relative_prices, 2)).\nYou should either decrease \ From 952e683c1b9f342d7e80379e0d5c1cceac8eda2d Mon Sep 17 00:00:00 2001 From: Shayan Date: Wed, 6 Mar 2024 11:56:30 +0330 Subject: [PATCH 2/5] [PM.md] Example Adapted to the new API 10316dcfeb01a0e1ce1f2a8d8280135a3e4c0ab5 --- docs/src/PM.md | 53 +++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/docs/src/PM.md b/docs/src/PM.md index 72485b9f..97d6dd9f 100644 --- a/docs/src/PM.md +++ b/docs/src/PM.md @@ -31,33 +31,34 @@ julia> prices = prices |> permutedims; # run the algorithm on the last 5 days julia> horizon, w = 5, 10 +julia> rel_prices = prices[:, 2:end] ./ prices[:, 1:end-1] + julia> model = cornu(prices, horizon, w) +julia> model = cornu(rel_prices, horizon, w) julia> model.b 5×5 Matrix{Float64}: - 0.0 0.198536 0.0995612 0.0 0.0 - 0.0 0.389272 0.0 0.0 0.0980504 - 0.0 0.0 0.430479 0.0998267 0.0 - 0.714743 0.0 0.0 0.0 0.203183 - 0.285257 0.412192 0.46996 0.900173 0.698766 + 0.759879 0.37932 0.518244 0.841716 0.538972 + 0.0600301 0.0798703 0.0198005 0.0395709 0.0397726 + 0.0600301 0.0798703 0.0198005 0.0395709 0.0397726 + 0.0600301 0.0798703 0.0198005 0.0395709 0.0397726 + 0.0600301 0.381069 0.422354 0.0395709 0.34171 ``` One can compute the cumulative wealth during the investment period by using the [`sn`](@ref) function: ```julia -julia> rel_price = prices[:, 2:end] ./ prices[:, 1:end-1]; - -julia> sn(model.b, rel_price) +julia> sn(model.b, rel_prices) 6-element Vector{Float64}: 1.0 - 0.9910701218600744 - 0.9956345799968089 - 1.0038929232387375 - 0.9914403615208097 - 0.9851289224781754 + 0.9874863981778458 + 0.9867337486209383 + 0.997125392069827 + 0.9899191819701306 + 0.9791598729100073 ``` -The result indicates that the algorithm experienced a loss of 1.5% of the initial wealth during the investment period. Further analysis of the algorithm's performance can be conducted using the [`mer`](@ref), [`ir`](@ref), [`apy`](@ref), [`ann_sharpe`](@ref), [`ann_std`](@ref), [`calmar`](@ref), and [`mdd`](@ref) functions. For more detailed information, refer to the [Performance evaluation](@ref) section. +The result indicates that the algorithm experienced a loss of ~2% of the initial wealth during the investment period. Further analysis of the algorithm's performance can be conducted using the [`mer`](@ref), [`ir`](@ref), [`apy`](@ref), [`ann_sharpe`](@ref), [`ann_std`](@ref), [`calmar`](@ref), and [`mdd`](@ref) functions. For more detailed information, refer to the [Performance evaluation](@ref) section. ### Run CORN-K @@ -67,27 +68,31 @@ The key parameters of CORN-K include `k` (number of best experts used for portfo # run the algorithm on the last 5 days julia> horizon, k, w, rho = 5, 10, 5, 5, 5; -julia> model = cornk(prices, horizon, k, w, rho); +julia> model = cornk(rel_prices, horizon, k, w, rho); julia> model.b +5×5 Matrix{Float64}: + 0.679862 0.279356 0.681348 0.581841 0.678181 + 0.0800344 0.0798487 0.0796631 0.0795637 0.079953 + 0.0800344 0.0798487 0.0796631 0.0795637 0.079953 + 0.0800344 0.0798487 0.0796631 0.0795637 0.079953 + 0.0800344 0.481098 0.0796631 0.179468 0.0819602 ``` Last but not least, the cumulative wealth of the algorithm on the investment period and given dataset can be computed by using the [`sn`](@ref) function: ```julia -julia> rel_price = prices[:, 2:end] ./ prices[:, 1:end-1]; - -julia> sn(model.b, rel_price) +julia> sn(model.b, rel_prices) 6-element Vector{Float64}: 1.0 - 0.9920219584145965 - 0.997769753240107 - 1.0153550964116513 - 1.004610801506029 - 1.0017637293758395 + 0.9875572675972655 + 0.9873565694624061 + 0.9913102075729211 + 0.9827281883555271 + 0.9722491752324016 ``` -Expectedly, CORN-K performed better than CORN-U on the same dataset. The result indicates that the algorithm has gained ~0.18% of the initial wealth during the investment period. Further analysis of the algorithm can be done by using the [`mer`](@ref), [`ir`](@ref), [`apy`](@ref), [`ann_sharpe`](@ref), [`ann_std`](@ref), [`calmar`](@ref), and [`mdd`](@ref) functions. See [Performance evaluation](@ref) section for more information. +Expectedly, CORN-K performed better than CORN-U on the same dataset. The result indicates that the algorithm has lost ~3.1% of the initial wealth during the investment period. Further analysis of the algorithm can be done by using the [`mer`](@ref), [`ir`](@ref), [`apy`](@ref), [`ann_sharpe`](@ref), [`ann_std`](@ref), [`calmar`](@ref), and [`mdd`](@ref) functions. See [Performance evaluation](@ref) section for more information. ## Dynamic RIsk CORrelation-driven Non-parametric (DRICORN) From e2c6f2f0e1527f72a4e0f4d8848841bb33d0ab69 Mon Sep 17 00:00:00 2001 From: Shayan Date: Wed, 6 Mar 2024 11:56:42 +0330 Subject: [PATCH 3/5] [index.md] Example Adapted to the new API 10316dcfeb01a0e1ce1f2a8d8280135a3e4c0ab5 --- docs/src/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 0299839c..4b584002 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -79,6 +79,9 @@ julia> pr = CSV.read("data\\sp500.csv", DataFrame) |> Matrix |> permutedims; julia> pr = pr[2:end, :]; +# calculate the relative prices +julia> rel_pr = pr[:, 2:end] ./ pr[:, 1:end-1]; + julia> market_pr = pr[1, :]; julia> rel_pr_market = market_pr[2:end] ./ market_pr[1:end-1]; @@ -90,9 +93,9 @@ julia> size(pr) The dataset encompasses adjusted close prices of 24 stocks in the S&P 500 across 1276 trading days. Suppose we aim to apply the strategies to the most recent 50 days of the dataset using default arguments: ```julia -julia> m_corn_u = cornu(pr, 50, 3); +julia> m_corn_u = cornu(rel_pr, 50, 3); -julia> m_corn_k = cornk(pr, 50, 3, 2, 2); +julia> m_corn_k = cornk(rel_pr, 50, 3, 2, 2); juila> m_drcorn_k = dricornk(pr, market_pr, 50, 5, 5, 5); ``` @@ -100,9 +103,6 @@ juila> m_drcorn_k = dricornk(pr, market_pr, 50, 5, 5, 5); Next, let's visualize the daily cumulative budgets' trends for each algorithm. To do this, we'll need to compute them by utilizing the attained portfolio weights and relative prices within the same time period. ```julia -# calculate the relative prices -julia> rel_pr = pr[:, 2:end] ./ pr[:, 1:end-1]; - julia> models = [m_corn_u, m_corn_k, m_drcorn_k]; # calculate the cumulative wealth for each algorithm From 632ad6ec3c5191997a35be258eea8d8479ae3291 Mon Sep 17 00:00:00 2001 From: Shayan Date: Wed, 6 Mar 2024 17:39:35 +0330 Subject: [PATCH 4/5] =?UTF-8?q?[CORN.jl]=20Minor=20docstrting=20enhancemen?= =?UTF-8?q?t=20=F0=9F=94=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Algos/CORN.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Algos/CORN.jl b/src/Algos/CORN.jl index eee67bcc..97166484 100644 --- a/src/Algos/CORN.jl +++ b/src/Algos/CORN.jl @@ -104,7 +104,7 @@ Run CORN-K algorithm. `x` should be a matrix of size `n_assets` × `n_periods`. # Returns -- `::OPSAlgorithm(n_assets, b, alg)`: An object of type `OPSAlgorithm`. +- `::OPSAlgorithm`: An object of type [`OPSAlgorithm`](@ref). # Examples ```julia From 8c364b4ac3d4e12850741f4a0815325863fe6954 Mon Sep 17 00:00:00 2001 From: Shayan Date: Wed, 6 Mar 2024 17:54:30 +0330 Subject: [PATCH 5/5] =?UTF-8?q?[CORN.jl]=20A=20technical=20error=20fixed?= =?UTF-8?q?=20=F0=9F=90=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Algos/CORN.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Algos/CORN.jl b/src/Algos/CORN.jl index 97166484..4eca2c36 100644 --- a/src/Algos/CORN.jl +++ b/src/Algos/CORN.jl @@ -150,11 +150,11 @@ function cornk( for t ∈ 0:horizon-1 expert = 1 for ω ∈ 1:w - for (idxρ, ρ) ∈ enumerate(P) + for ρ ∈ P b = corn_expert(x, horizon, ω, ρ, t, n_assets) - bₜ[:, ω+idxρ] = b - Sₜ_[ω+idxρ, t+2] = S( - Sₜ_[ω+idxρ, t+1], b, x[:, end-horizon+t+1] + bₜ[:, expert] = b + Sₜ_[expert, t+2] = S( + Sₜ_[expert, t+1], b, x[:, end-horizon+t+1] ) expert += 1 end