Title: | Statistical Significance of the Sharpe Ratio |
---|---|
Description: | A collection of tools for analyzing significance of assets, funds, and trading strategies, based on the Sharpe ratio and overfit of the same. Provides density, distribution, quantile and random generation of the Sharpe ratio distribution based on normal returns, as well as the optimal Sharpe ratio over multiple assets. Computes confidence intervals on the Sharpe and provides a test of equality of Sharpe ratios based on the Delta method. The statistical foundations of the Sharpe can be found in the author's Short Sharpe Course <doi:10.2139/ssrn.3036276>. |
Authors: | Steven E. Pav [aut, cre] |
Maintainer: | Steven E. Pav <[email protected]> |
License: | LGPL-3 |
Version: | 1.3.0 |
Built: | 2024-11-08 03:11:53 UTC |
Source: | https://github.com/shabbychef/SharpeR |
Inference on Sharpe ratio and Markowitz portfolio.
Suppose are
independent draws of a normal random
variable with mean
and variance
.
Let
be the sample mean, and
be
the sample standard deviation (using Bessel's correction). Let
be the 'risk free' or 'disastrous rate' of return. Then
is the (sample) Sharpe ratio.
The units of are
.
Typically the Sharpe ratio is annualized by multiplying by
, where
is the number of observations
per year (or whatever the target annualization epoch.) It is not
common practice to include units when quoting Sharpe ratio, though
doing so could avoid confusion.
The Sharpe ratio follows a rescaled non-central t distribution. That
is, follows a non-central t-distribution
with
degrees of freedom and non-centrality parameter
, for some
,
and
.
We can generalize Sharpe's model to APT, wherein we write
where the are observed 'factor returns', and
the variance of the noise term is
.
Via linear regression, one can compute estimates
,
and
, and then let the 'Sharpe ratio' be
As above, this Sharpe ratio follows a rescaled t-distribution under normality, etc.
The parameters are encoded as follows:
df
stands for the degrees of freedom, typically , but
in general.
is denoted by
zeta
.
is denoted by
ope
. ('Observations Per Year')
For the APT form of Sharpe, K
stands for the
rescaling parameter.
Suppose are
independent draws of a
-variate
normal random variable with mean
and covariance matrix
. Let
be the (vector) sample mean, and
be the sample covariance matrix (using Bessel's correction). Let
be the (sample) Sharpe ratio of the portfolio , subject to
risk free rate
.
Let be the solution to the portfolio optimization problem:
with maximum value .
Then
and
The variable follows an Optimal Sharpe ratio
distribution. For convenience, we may assume that the sample statistic
has been annualized in the same manner as the Sharpe ratio, that is
by multiplying by
, the number of observations per
epoch.
The Optimal Sharpe Ratio distribution is parametrized by the number
of assets, , the number of independent observations,
, the
noncentrality parameter,
the 'drag' term, , and the annualization factor,
.
The drag term makes this a location family of distributions, and
by default we assume it is zero.
The parameters are encoded as follows:
is denoted by
df1
.
is denoted by
df2
.
is denoted by
zeta.s
.
is denoted by
ope
.
is denoted by
drag
.
As above, let
be the (sample) Sharpe ratio of the portfolio , subject to
risk free rate
.
Let be a
matrix of 'hedge constraints'.
Let
be the solution to the portfolio optimization problem:
with maximum value .
Then
can be expressed as the difference of two squared
optimal Sharpe ratio random variables. A monotonic transform takes this
difference to the LRT statistic for portfolio spanning, first described by
Rao, and refined by Giri.
SharpeR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
The following are still in the works:
Corrections for standard error based on skew, kurtosis and autocorrelation.
Tests on Sharpe under positivity constraint. (c.f. Silvapulle)
Portfolio spanning tests.
Tests on portfolio weights.
This package is maintained as a hobby.
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Johnson, N. L., and Welch, B. L. "Applications of the non-central t-distribution." Biometrika 31, no. 3-4 (1940): 362-389. doi:10.1093/biomet/31.3-4.362
Lo, Andrew W. "The statistics of Sharpe ratios." Financial Analysts Journal 58, no. 4 (2002): 36-52. https://www.ssrn.com/paper=377260
Opdyke, J. D. "Comparing Sharpe Ratios: So Where are the p-values?" Journal of Asset Management 8, no. 5 (2006): 308-336. https://www.ssrn.com/paper=886728
Ledoit, O., and Wolf, M. "Robust performance hypothesis testing with the Sharpe ratio." Journal of Empirical Finance 15, no. 5 (2008): 850-859. doi:10.1016/j.jempfin.2008.03.002
Giri, N. "On the likelihood ratio test of a normal multivariate testing problem." Annals of Mathematical Statistics 35, no. 1 (1964): 181-189. doi:10.1214/aoms/1177703740
Rao, C. R. "Advanced Statistical Methods in Biometric Research." Wiley (1952).
Rao, C. R. "On Some Problems Arising out of Discrimination with Multiple Characters." Sankhya, 9, no. 4 (1949): 343-366. https://www.jstor.org/stable/25047988
Kan, Raymond and Smith, Daniel R. "The Distribution of the Sample Minimum-Variance Frontier." Journal of Management Science 54, no. 7 (2008): 1364–1380. doi:10.1287/mnsc.1070.0852
Kan, Raymond and Zhou, GuoFu. "Tests of Mean-Variance Spanning." Annals of Economics and Finance 13, no. 1 (2012) https://econpapers.repec.org/article/cufjournl/y_3a2012_3av_3a13_3ai_3a1_3akanzhou.htm
Britten-Jones, Mark. "The Sampling Error in Estimates of Mean-Variance Efficient Portfolio Weights." The Journal of Finance 54, no. 2 (1999): 655–671. https://www.jstor.org/stable/2697722
Silvapulle, Mervyn. J. "A Hotelling's T2-type statistic for testing against one-sided hypotheses." Journal of Multivariate Analysis 55, no. 2 (1995): 312–319. doi:10.1006/jmva.1995.1081
Bodnar, Taras and Okhrin, Yarema. "On the Product of Inverse Wishart and Normal Distributions with Applications to Discriminant Analysis and Portfolio Theory." Scandinavian Journal of Statistics 38, no. 2 (2011): 311–331. doi:10.1111/j.1467-9469.2011.00729.x
Computes the Sharpe ratio of the hedged Markowitz portfolio of some observed returns.
as.del_sropt(X, G, drag = 0, ope = 1, epoch = "yr") ## Default S3 method: as.del_sropt(X, G, drag = 0, ope = 1, epoch = "yr") ## S3 method for class 'xts' as.del_sropt(X, G, drag = 0, ope = 1, epoch = "yr")
as.del_sropt(X, G, drag = 0, ope = 1, epoch = "yr") ## Default S3 method: as.del_sropt(X, G, drag = 0, ope = 1, epoch = "yr") ## S3 method for class 'xts' as.del_sropt(X, G, drag = 0, ope = 1, epoch = "yr")
X |
matrix of returns, or |
G |
an |
drag |
the 'drag' term, |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
epoch |
the string representation of the 'epoch', defaulting to 'yr'. |
Suppose are
independent draws of a
-variate
normal random variable with mean
and covariance matrix
. Let
be a
matrix
of rank
.
Let
be the (vector) sample mean, and
be the sample covariance matrix (using Bessel's correction).
Let
be the (sample) Sharpe ratio of the portfolio , subject to
risk free rate
.
Let be the solution to the portfolio optimization
problem:
with maximum value .
Note that if ope
and epoch
are not given, the
converter from xts
attempts to infer the observations per epoch,
assuming yearly epoch.
An object of class del_sropt
.
Steven E. Pav [email protected]
Other del_sropt:
del_sropt
,
is.del_sropt()
nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) # hedge out the first one: G <- matrix(diag(nfac)[1,],nrow=1) asro <- as.del_sropt(Returns,G,drag=0,ope=ope) print(asro) G <- diag(nfac)[c(1:3),] asro <- as.del_sropt(Returns,G,drag=0,ope=ope) # compare to sropt on the remaining assets # they should be close, but not exact. asro.alt <- as.sropt(Returns[,4:nfac],drag=0,ope=ope) # using real data. if (require(xts)) { data(stock_returns) # hedge out SPY G <- diag(dim(stock_returns)[2])[3,] asro <- as.del_sropt(stock_returns,G=G) }
nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) # hedge out the first one: G <- matrix(diag(nfac)[1,],nrow=1) asro <- as.del_sropt(Returns,G,drag=0,ope=ope) print(asro) G <- diag(nfac)[c(1:3),] asro <- as.del_sropt(Returns,G,drag=0,ope=ope) # compare to sropt on the remaining assets # they should be close, but not exact. asro.alt <- as.sropt(Returns[,4:nfac],drag=0,ope=ope) # using real data. if (require(xts)) { data(stock_returns) # hedge out SPY G <- diag(dim(stock_returns)[2])[3,] asro <- as.del_sropt(stock_returns,G=G) }
Computes the Sharpe ratio of some observed returns.
as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## Default S3 method: as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'matrix' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'data.frame' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'lm' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'xts' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'timeSeries' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE)
as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## Default S3 method: as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'matrix' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'data.frame' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'lm' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'xts' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE) ## S3 method for class 'timeSeries' as.sr(x, c0 = 0, ope = 1, na.rm = FALSE, epoch = "yr", higher_order = FALSE)
x |
vector of returns, or object of class |
c0 |
the 'risk-free' or 'disastrous' rate of return. this is assumed to be given in the same units as x, not in 'annualized' terms. |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
na.rm |
logical. Should missing values be removed? |
epoch |
the string representation of the 'epoch', defaulting to 'yr'. |
higher_order |
a Boolean. If true, we compute cumulants of the returns to leverage higher order accuracy formulae when possible. |
Suppose are
independent returns of some
asset.
Let
be the sample mean, and
be
the sample standard deviation (using Bessel's correction). Let
be the 'risk free rate'. Then
is the (sample) Sharpe ratio.
The units of are
.
Typically the Sharpe ratio is annualized by multiplying by
, where
is the number of observations
per year (or whatever the target annualization epoch.)
Note that if ope
is not given, the converter from xts
attempts to infer the observations per year, without regard to
the name of the epoch
given.
a list containing the following components:
the annualized Sharpe ratio.
the t-stat degrees of freedom.
the risk free term.
the annualization factor.
the rescaling factor.
the string epoch.
cast to class sr
.
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Lo, Andrew W. "The statistics of Sharpe ratios." Financial Analysts Journal 58, no. 4 (2002): 36-52. https://www.ssrn.com/paper=377260
sr-distribution functions, dsr, psr, qsr, rsr
Other sr:
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
# Sharpe's 'model': just given a bunch of returns. asr <- as.sr(rnorm(253*3),ope=253) # or a matrix, with a name my.returns <- matrix(rnorm(253*3),ncol=1) colnames(my.returns) <- c("my strategy") asr <- as.sr(my.returns) # given an xts object: if (require(xts)) { data(stock_returns) IBM <- stock_returns[,'IBM'] asr <- as.sr(IBM,na.rm=TRUE) } # on a linear model, find the 'Sharpe' of the residual term nfac <- 5 nyr <- 10 ope <- 253 set.seed(as.integer(charToRaw("determinstic"))) Factors <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) Betas <- exp(0.1 * rnorm(dim(Factors)[2])) Returns <- (Factors %*% Betas) + rnorm(dim(Factors)[1],mean=0.0005,sd=0.012) APT_mod <- lm(Returns ~ Factors) asr <- as.sr(APT_mod,ope=ope) # try again, but make the Returns independent of the Factors. Returns <- rnorm(dim(Factors)[1],mean=0.0005,sd=0.012) APT_mod <- lm(Returns ~ Factors) asr <- as.sr(APT_mod,ope=ope) # compute the Sharpe of a bunch of strategies: my.returns <- matrix(rnorm(253*3*4),ncol=4) asr <- as.sr(my.returns) # without sensible colnames? colnames(my.returns) <- c("strat a","strat b","strat c","strat d") asr <- as.sr(my.returns)
# Sharpe's 'model': just given a bunch of returns. asr <- as.sr(rnorm(253*3),ope=253) # or a matrix, with a name my.returns <- matrix(rnorm(253*3),ncol=1) colnames(my.returns) <- c("my strategy") asr <- as.sr(my.returns) # given an xts object: if (require(xts)) { data(stock_returns) IBM <- stock_returns[,'IBM'] asr <- as.sr(IBM,na.rm=TRUE) } # on a linear model, find the 'Sharpe' of the residual term nfac <- 5 nyr <- 10 ope <- 253 set.seed(as.integer(charToRaw("determinstic"))) Factors <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) Betas <- exp(0.1 * rnorm(dim(Factors)[2])) Returns <- (Factors %*% Betas) + rnorm(dim(Factors)[1],mean=0.0005,sd=0.012) APT_mod <- lm(Returns ~ Factors) asr <- as.sr(APT_mod,ope=ope) # try again, but make the Returns independent of the Factors. Returns <- rnorm(dim(Factors)[1],mean=0.0005,sd=0.012) APT_mod <- lm(Returns ~ Factors) asr <- as.sr(APT_mod,ope=ope) # compute the Sharpe of a bunch of strategies: my.returns <- matrix(rnorm(253*3*4),ncol=4) asr <- as.sr(my.returns) # without sensible colnames? colnames(my.returns) <- c("strat a","strat b","strat c","strat d") asr <- as.sr(my.returns)
Computes the Sharpe ratio of the Markowitz portfolio of some observed returns.
as.sropt(X, drag = 0, ope = 1, epoch = "yr") ## Default S3 method: as.sropt(X, drag = 0, ope = 1, epoch = "yr") ## S3 method for class 'xts' as.sropt(X, drag = 0, ope = 1, epoch = "yr")
as.sropt(X, drag = 0, ope = 1, epoch = "yr") ## Default S3 method: as.sropt(X, drag = 0, ope = 1, epoch = "yr") ## S3 method for class 'xts' as.sropt(X, drag = 0, ope = 1, epoch = "yr")
X |
matrix of returns, or |
drag |
the 'drag' term, |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
epoch |
the string representation of the 'epoch', defaulting to 'yr'. |
Suppose are
independent draws of a
-variate
normal random variable with mean
and covariance matrix
. Let
be the (vector) sample mean, and
be the sample covariance matrix (using Bessel's correction). Let
be the (sample) Sharpe ratio of the portfolio , subject to
risk free rate
.
Let be the solution to the portfolio optimization problem:
with maximum value .
Then
and
The units of are
.
Typically the Sharpe ratio is annualized by multiplying by
, where
is the number of observations
per year (or whatever the target annualization epoch.)
Note that if ope
and epoch
are not given, the
converter from xts
attempts to infer the observations per epoch,
assuming yearly epoch.
An object of class sropt
.
Steven E. Pav [email protected]
sropt
, sr
, sropt-distribution functions,
dsropt, psropt, qsropt, rsropt
Other sropt:
confint.sr()
,
dsropt()
,
is.sropt()
,
pco_sropt()
,
power.sropt_test()
,
reannualize()
,
sropt_test()
,
sropt
nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) # under the alternative: Returns <- matrix(rnorm(ope*nyr*nfac,mean=0.0005,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) # generating correlated multivariate normal data in a more sane way if (require(MASS)) { nstok <- 10 nfac <- 3 nyr <- 10 ope <- 253 X.like <- 0.01 * matrix(rnorm(500*nfac),ncol=nfac) %*% matrix(runif(nfac*nstok),ncol=nstok) Sigma <- cov(X.like) + diag(0.003,nstok) # under the null: Returns <- mvrnorm(ceiling(ope*nyr),mu=matrix(0,ncol=nstok),Sigma=Sigma) asro <- as.sropt(Returns,ope=ope) # under the alternative Returns <- mvrnorm(ceiling(ope*nyr),mu=matrix(0.001,ncol=nstok),Sigma=Sigma) asro <- as.sropt(Returns,ope=ope) } # using real data. if (require(xts)) { data(stock_returns) asro <- as.sropt(stock_returns) }
nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) # under the alternative: Returns <- matrix(rnorm(ope*nyr*nfac,mean=0.0005,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) # generating correlated multivariate normal data in a more sane way if (require(MASS)) { nstok <- 10 nfac <- 3 nyr <- 10 ope <- 253 X.like <- 0.01 * matrix(rnorm(500*nfac),ncol=nfac) %*% matrix(runif(nfac*nstok),ncol=nstok) Sigma <- cov(X.like) + diag(0.003,nstok) # under the null: Returns <- mvrnorm(ceiling(ope*nyr),mu=matrix(0,ncol=nstok),Sigma=Sigma) asro <- as.sropt(Returns,ope=ope) # under the alternative Returns <- mvrnorm(ceiling(ope*nyr),mu=matrix(0.001,ncol=nstok),Sigma=Sigma) asro <- as.sropt(Returns,ope=ope) } # using real data. if (require(xts)) { data(stock_returns) asro <- as.sropt(stock_returns) }
Computes approximate confidence intervals on the (optimal) Signal-Noise ratio
given the (optimal) Sharpe ratio.
Works on objects of class sr
and sropt
.
## S3 method for class 'sr' confint( object, parm, level = 0.95, level.lo = (1 - level)/2, level.hi = 1 - level.lo, type = c("exact", "t", "Z", "Mertens", "Bao"), ... ) ## S3 method for class 'sropt' confint( object, parm, level = 0.95, level.lo = (1 - level)/2, level.hi = 1 - level.lo, ... ) ## S3 method for class 'del_sropt' confint( object, parm, level = 0.95, level.lo = (1 - level)/2, level.hi = 1 - level.lo, ... )
## S3 method for class 'sr' confint( object, parm, level = 0.95, level.lo = (1 - level)/2, level.hi = 1 - level.lo, type = c("exact", "t", "Z", "Mertens", "Bao"), ... ) ## S3 method for class 'sropt' confint( object, parm, level = 0.95, level.lo = (1 - level)/2, level.hi = 1 - level.lo, ... ) ## S3 method for class 'del_sropt' confint( object, parm, level = 0.95, level.lo = (1 - level)/2, level.hi = 1 - level.lo, ... )
object |
an observed Sharpe ratio statistic, of class |
parm |
ignored here, but required for the general method. |
level |
the confidence level required. |
level.lo |
the lower confidence level required. |
level.hi |
the upper confidence level required. |
type |
which method to apply. |
... |
further arguments to be passed to or from methods. |
Constructs confidence intervals on the Signal-Noise ratio given observed Sharpe ratio statistic. The available methods are:
The default, which is only exact when returns are normal, based on inverting the non-central t distribution.
Uses the Johnson Welch approximation to the standard error, centered around the sample value.
Uses the Johnson Welch approximation to the standard error, performing a simple correction for the bias of the Sharpe ratio based on Miller and Gehr formula.
Uses the Mertens higher order approximation to the standard error, centered around the sample value.
Uses the Bao higher order approximation to the standard error, performing a higher order correction for the bias of the Sharpe ratio.
Suppose are
independent draws of a
-variate
normal random variable with mean
and covariance matrix
. Let
be the (vector) sample mean, and
be the sample covariance matrix (using Bessel's correction).
Let
Given observations of , compute confidence intervals on the
population analogue, defined as
A matrix (or vector) with columns giving lower and upper
confidence limits for the parameter. These will be labelled as
level.lo and level.hi in %, e.g. "2.5 %"
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Other sr:
as.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
Other sropt:
as.sropt()
,
dsropt()
,
is.sropt()
,
pco_sropt()
,
power.sropt_test()
,
reannualize()
,
sropt_test()
,
sropt
# using "sr" class: ope <- 253 df <- ope * 6 xv <- rnorm(df, 1 / sqrt(ope)) mysr <- as.sr(xv,ope=ope) confint(mysr,level=0.90) # using "lm" class yv <- xv + rnorm(length(xv)) amod <- lm(yv ~ xv) mysr <- as.sr(amod,ope=ope) confint(mysr,level.lo=0.05,level.hi=1.0) # rolling your own. ope <- 253 df <- ope * 6 zeta <- 1.0 rvs <- rsr(128, df, zeta, ope) roll.own <- sr(sr=rvs,df=df,c0=0,ope=ope) aci <- confint(roll.own,level=0.95) coverage <- 1 - mean((zeta < aci[,1]) | (aci[,2] < zeta)) # using "sropt" class ope <- 253 df1 <- 4 df2 <- ope * 3 rvs <- as.matrix(rnorm(df1*df2),ncol=df1) sro <- as.sropt(rvs,ope=ope) aci <- confint(sro) # on sropt, rolling your own. zeta.s <- 1.0 rvs <- rsropt(128, df1, df2, zeta.s, ope) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope) aci <- confint(roll.own,level=0.95) coverage <- 1 - mean((zeta.s < aci[,1]) | (aci[,2] < zeta.s)) # using "del_sropt" class nfac <- 5 nyr <- 10 ope <- 253 set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) # hedge out the first one: G <- matrix(diag(nfac)[1,],nrow=1) asro <- as.del_sropt(Returns,G,drag=0,ope=ope) aci <- confint(asro,level=0.95) # under the alternative Returns <- matrix(rnorm(ope*nyr*nfac,mean=0.001,sd=0.0125),ncol=nfac) asro <- as.del_sropt(Returns,G,drag=0,ope=ope) aci <- confint(asro,level=0.95)
# using "sr" class: ope <- 253 df <- ope * 6 xv <- rnorm(df, 1 / sqrt(ope)) mysr <- as.sr(xv,ope=ope) confint(mysr,level=0.90) # using "lm" class yv <- xv + rnorm(length(xv)) amod <- lm(yv ~ xv) mysr <- as.sr(amod,ope=ope) confint(mysr,level.lo=0.05,level.hi=1.0) # rolling your own. ope <- 253 df <- ope * 6 zeta <- 1.0 rvs <- rsr(128, df, zeta, ope) roll.own <- sr(sr=rvs,df=df,c0=0,ope=ope) aci <- confint(roll.own,level=0.95) coverage <- 1 - mean((zeta < aci[,1]) | (aci[,2] < zeta)) # using "sropt" class ope <- 253 df1 <- 4 df2 <- ope * 3 rvs <- as.matrix(rnorm(df1*df2),ncol=df1) sro <- as.sropt(rvs,ope=ope) aci <- confint(sro) # on sropt, rolling your own. zeta.s <- 1.0 rvs <- rsropt(128, df1, df2, zeta.s, ope) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope) aci <- confint(roll.own,level=0.95) coverage <- 1 - mean((zeta.s < aci[,1]) | (aci[,2] < zeta.s)) # using "del_sropt" class nfac <- 5 nyr <- 10 ope <- 253 set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) # hedge out the first one: G <- matrix(diag(nfac)[1,],nrow=1) asro <- as.del_sropt(Returns,G,drag=0,ope=ope) aci <- confint(asro,level=0.95) # under the alternative Returns <- matrix(rnorm(ope*nyr*nfac,mean=0.001,sd=0.0125),ncol=nfac) asro <- as.del_sropt(Returns,G,drag=0,ope=ope) aci <- confint(asro,level=0.95)
Spawns an object of class del_sropt
.
del_sropt(z.s, z.sub, df1, df2, df1.sub, drag = 0, ope = 1, epoch = "yr")
del_sropt(z.s, z.sub, df1, df2, df1.sub, drag = 0, ope = 1, epoch = "yr")
z.s |
an optimum Sharpe ratio statistic, on some set of assets. |
z.sub |
an optimum Sharpe ratio statistic, on a linear subspace
of the assets. If larger than |
df1 |
the number of assets in the portfolio. |
df2 |
the number of observations. |
df1.sub |
the rank of the linear subspace of the hedge constraint. by restricting attention to the subspace. |
drag |
the 'drag' term, |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
epoch |
the string representation of the 'epoch', defaulting to 'yr'. |
The del_sropt
class contains information about the difference
between two rescaled T^2-statistics, useful for spanning
tests, and inference on hedged portfolios.
The following are list attributes of the object:
The (optimal) Sharpe ratio statistic of the 'full' set of assets.
The (optimal) Sharpe ratio statistic on some subset, or linear subspace, of the assets.
The number of assets.
The number of observations.
The number of degrees of freedom in the hedge constraint.
The drag term, which is the 'risk free rate' divided by the maximum risk.
The 'observations per epoch'.
The string name of the 'epoch'.
For the most part, this constructor should not be called directly,
rather as.del_sropt
should be called instead to compute the
needed statistics.
a list cast to class del_sropt
, with attributes
the optimal Sharpe statistic.
the optimal Sharpe statistic on the subspace.
the number of assets.
the number of observed vectors.
the input df1.sub
term.
the input drag
term.
the input ope
term.
the Hotelling statistic.
the Hotelling statistic on the subspace.
WARNING: This function is not well tested, may contain errors, may change in the next package update. Take caution.
2FIX: allow rownames?
Steven E. Pav [email protected]
Other del_sropt:
as.del_sropt()
,
is.del_sropt()
# roll your own. ope <- 253 set.seed(as.integer(charToRaw("be determinstic"))) n.stock <- 10 X <- matrix(rnorm(1000*n.stock),nrow=1000) Sigma <- cov(X) mu <- colMeans(X) w <- solve(Sigma,mu) z <- t(mu) %*% w n.sub <- 6 w.sub <- solve(Sigma[1:n.sub,1:n.sub],mu[1:n.sub]) z.sub <- t(mu[1:n.sub]) %*% w.sub df1.sub <- n.stock - n.sub roll.own <- del_sropt(z.s=z,z.sub=z.sub,df1=10,df2=1000, df1.sub=df1.sub,ope=ope) print(roll.own)
# roll your own. ope <- 253 set.seed(as.integer(charToRaw("be determinstic"))) n.stock <- 10 X <- matrix(rnorm(1000*n.stock),nrow=1000) Sigma <- cov(X) mu <- colMeans(X) w <- solve(Sigma,mu) z <- t(mu) %*% w n.sub <- 6 w.sub <- solve(Sigma[1:n.sub,1:n.sub],mu[1:n.sub]) z.sub <- t(mu[1:n.sub]) %*% w.sub df1.sub <- n.stock - n.sub roll.own <- del_sropt(z.s=z,z.sub=z.sub,df1=10,df2=1000, df1.sub=df1.sub,ope=ope) print(roll.own)
Density, distribution function, quantile function and random
generation for the Sharpe ratio distribution with df
degrees of freedom
(and optional signal-noise-ratio zeta
).
dsr(x, df, zeta, ope, ...) psr(q, df, zeta, ope, ...) qsr(p, df, zeta, ope, ...) rsr(n, df, zeta, ope)
dsr(x, df, zeta, ope, ...) psr(q, df, zeta, ope, ...) qsr(p, df, zeta, ope, ...) rsr(n, df, zeta, ope)
x , q
|
vector of quantiles. |
df |
the number of observations the statistic is based on. This
is one more than the number of degrees of freedom in the
corresponding t-statistic, although the effect will be small
when |
zeta |
the 'signal-to-noise' parameter, |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
... |
arguments passed on to the respective t-distribution functions, namely
|
p |
vector of probabilities. |
n |
number of observations. |
Suppose are
independent draws of a normal random
variable with mean
and variance
.
Let
be the sample mean, and
be
the sample standard deviation (using Bessel's correction). Let
be the 'risk free rate'. Then
is the (sample) Sharpe ratio.
The units of is
.
Typically the Sharpe ratio is annualized by multiplying by
, where
is the number of observations
per epoch (typically a year).
Letting ,
where the sample estimates are based on
observations,
then
takes a (non-central) Sharpe ratio distribution
parametrized by
'degrees of freedom', non-centrality parameter
, and
annualization parameter
.
The parameters are encoded as follows:
is denoted by
df
.
is denoted by
zeta
.
is denoted by
ope
. ('Observations Per Year')
If the returns violate the assumptions of normality, independence, etc (as they always should in the real world), the sample Sharpe Ratio will not follow this distribution. It does provide, however, a reasonable approximation in many cases.
See ‘The Sharpe Ratio: Statistics and Applications’, section 2.2.
dsr
gives the density, psr
gives the distribution function,
qsr
gives the quantile function, and rsr
generates random deviates.
Invalid arguments will result in return value NaN
with a warning.
This is a thin wrapper on the t distribution.
The functions dt, pt, qt
can accept ncp from
limited range (). Some corrections
may have to be made here for large
zeta
.
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
t-distribution functions, dt, pt, qt, rt
Other sr:
as.sr()
,
confint.sr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
rvs <- rsr(128, 253*6, 0, 253) dvs <- dsr(rvs, 253*6, 0, 253) pvs.H0 <- psr(rvs, 253*6, 0, 253) pvs.HA <- psr(rvs, 253*6, 1, 253) plot(ecdf(pvs.H0)) plot(ecdf(pvs.HA))
rvs <- rsr(128, 253*6, 0, 253) dvs <- dsr(rvs, 253*6, 0, 253) pvs.H0 <- psr(rvs, 253*6, 0, 253) pvs.HA <- psr(rvs, 253*6, 1, 253) plot(ecdf(pvs.H0)) plot(ecdf(pvs.HA))
Density, distribution function, quantile function and random
generation for the maximal Sharpe ratio distribution with
df1
and df2
degrees of freedom
(and optional maximal signal-noise-ratio zeta.s
).
dsropt(x, df1, df2, zeta.s, ope, drag = 0, log = FALSE) psropt(q, df1, df2, zeta.s, ope, drag = 0, ...) qsropt(p, df1, df2, zeta.s, ope, drag = 0, ...) rsropt(n, df1, df2, zeta.s, ope, drag = 0, ...)
dsropt(x, df1, df2, zeta.s, ope, drag = 0, log = FALSE) psropt(q, df1, df2, zeta.s, ope, drag = 0, ...) qsropt(p, df1, df2, zeta.s, ope, drag = 0, ...) rsropt(n, df1, df2, zeta.s, ope, drag = 0, ...)
x , q
|
vector of quantiles. |
df1 |
the number of assets in the portfolio. |
df2 |
the number of observations. |
zeta.s |
the non-centrality parameter, defined as
|
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
drag |
the 'drag' term, |
log |
logical; if TRUE, densities |
p |
vector of probabilities. |
n |
number of observations. |
... |
arguments passed on to the respective Hotelling |
Suppose are
independent draws of a
-variate
normal random variable with mean
and covariance matrix
. Let
be the (vector) sample mean, and
be the sample covariance matrix (using Bessel's correction). Let
be the (sample) Sharpe ratio of the portfolio , subject to
risk free rate
.
Let be the solution to the portfolio optimization problem:
with maximum value .
Then
and
The variable follows an Optimal Sharpe ratio
distribution. For convenience, we may assume that the sample statistic
has been annualized in the same manner as the Sharpe ratio, that is
by multiplying by
, the number of observations per
epoch.
The Optimal Sharpe Ratio distribution is parametrized by the number
of assets, , the number of independent observations,
, the
noncentrality parameter,
the 'drag' term, , and the annualization factor,
.
The drag term makes this a location family of distributions, and
by default we assume it is zero.
The parameters are encoded as follows:
is denoted by
df1
.
is denoted by
df2
.
is denoted by
zeta.s
.
is denoted by
ope
.
is denoted by
drag
.
See ‘The Sharpe Ratio: Statistics and Applications’, section 6.1.4.
dsropt
gives the density, psropt
gives the distribution function,
qsropt
gives the quantile function, and rsropt
generates random deviates.
Invalid arguments will result in return value NaN
with a warning.
This is a thin wrapper on the Hotelling T-squared distribution, which is a wrapper on the F distribution.
Steven E. Pav [email protected]
Kan, Raymond and Smith, Daniel R. "The Distribution of the Sample Minimum-Variance Frontier." Journal of Management Science 54, no. 7 (2008): 1364–1380. doi:10.1287/mnsc.1070.0852
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
F-distribution functions, df, pf, qf, rf
,
Sharpe ratio distribution, dsr, psr, qsr, rsr
.
Other sropt:
as.sropt()
,
confint.sr()
,
is.sropt()
,
pco_sropt()
,
power.sropt_test()
,
reannualize()
,
sropt_test()
,
sropt
# generate some variates ngen <- 128 ope <- 253 df1 <- 8 df2 <- ope * 10 drag <- 0 # sample rvs <- rsropt(ngen, df1, df2, drag, ope) hist(rvs) # these should be uniform: isp <- psropt(rvs, df1, df2, drag, ope) plot(ecdf(isp))
# generate some variates ngen <- 128 ope <- 253 df1 <- 8 df2 <- ope * 10 drag <- 0 # sample rvs <- rsropt(ngen, df1, df2, drag, ope) hist(rvs) # these should be uniform: isp <- psropt(rvs, df1, df2, drag, ope) plot(ecdf(isp))
Estimates the non-centrality parameter associated with an observed statistic following an optimal Sharpe Ratio distribution.
inference(z.s, type = c("KRS", "MLE", "unbiased")) ## S3 method for class 'sropt' inference(z.s, type = c("KRS", "MLE", "unbiased")) ## S3 method for class 'del_sropt' inference(z.s, type = c("KRS", "MLE", "unbiased"))
inference(z.s, type = c("KRS", "MLE", "unbiased")) ## S3 method for class 'sropt' inference(z.s, type = c("KRS", "MLE", "unbiased")) ## S3 method for class 'del_sropt' inference(z.s, type = c("KRS", "MLE", "unbiased"))
z.s |
an object of type |
type |
the estimator type. one of |
Let be an observed statistic distributed as a non-central F with
,
degrees of freedom and non-centrality
parameter
. Three methods are presented to
estimate the non-centrality parameter from the statistic:
an unbiased estimator, which, unfortunately, may be negative.
the Maximum Likelihood Estimator, which may be zero, but not negative.
the estimator of Kubokawa, Roberts, and Shaleh (KRS), which is a shrinkage estimator.
The sropt distribution is equivalent to an F distribution up to a square root and some rescalings.
The non-centrality parameter of the sropt distribution is
the square root of that of the Hotelling, i.e. has
units 'per square root time'. As such, the 'unbiased'
type can be problematic!
an estimate of the non-centrality parameter, which is the maximal population Sharpe ratio.
Steven E. Pav [email protected]
Kubokawa, T., C. P. Robert, and A. K. Saleh. "Estimation of noncentrality parameters." Canadian Journal of Statistics 21, no. 1 (1993): 45-57. https://www.jstor.org/stable/3315657
Spruill, M. C. "Computation of the maximum likelihood estimate of a noncentrality parameter." Journal of multivariate analysis 18, no. 2 (1986): 216-224. https://www.sciencedirect.com/science/article/pii/0047259X86900709
F-distribution functions, df
.
Other sropt Hotelling:
sric()
# generate some sropts nfac <- 3 nyr <- 5 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) est1 <- inference(asro,type='unbiased') est2 <- inference(asro,type='KRS') est3 <- inference(asro,type='MLE') # under the alternative: Returns <- matrix(rnorm(ope*nyr*nfac,mean=0.0005,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) est1 <- inference(asro,type='unbiased') est2 <- inference(asro,type='KRS') est3 <- inference(asro,type='MLE') # sample many under the alternative, look at the estimator. df1 <- 3 df2 <- 512 ope <- 253 zeta.s <- 1.25 rvs <- rsropt(128, df1, df2, zeta.s, ope) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope) est1 <- inference(roll.own,type='unbiased') est2 <- inference(roll.own,type='KRS') est3 <- inference(roll.own,type='MLE') # for del_sropt: nfac <- 5 nyr <- 10 ope <- 253 set.seed(as.integer(charToRaw("fix seed"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0.0005,sd=0.0125),ncol=nfac) # hedge out the first one: G <- matrix(diag(nfac)[1,],nrow=1) asro <- as.del_sropt(Returns,G,drag=0,ope=ope) est1 <- inference(asro,type='unbiased') est2 <- inference(asro,type='KRS') est3 <- inference(asro,type='MLE')
# generate some sropts nfac <- 3 nyr <- 5 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) est1 <- inference(asro,type='unbiased') est2 <- inference(asro,type='KRS') est3 <- inference(asro,type='MLE') # under the alternative: Returns <- matrix(rnorm(ope*nyr*nfac,mean=0.0005,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) est1 <- inference(asro,type='unbiased') est2 <- inference(asro,type='KRS') est3 <- inference(asro,type='MLE') # sample many under the alternative, look at the estimator. df1 <- 3 df2 <- 512 ope <- 253 zeta.s <- 1.25 rvs <- rsropt(128, df1, df2, zeta.s, ope) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope) est1 <- inference(roll.own,type='unbiased') est2 <- inference(roll.own,type='KRS') est3 <- inference(roll.own,type='MLE') # for del_sropt: nfac <- 5 nyr <- 10 ope <- 253 set.seed(as.integer(charToRaw("fix seed"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0.0005,sd=0.0125),ncol=nfac) # hedge out the first one: G <- matrix(diag(nfac)[1,],nrow=1) asro <- as.del_sropt(Returns,G,drag=0,ope=ope) est1 <- inference(asro,type='unbiased') est2 <- inference(asro,type='KRS') est3 <- inference(asro,type='MLE')
Checks if an object is in the class 'del_sropt'
is.del_sropt(x)
is.del_sropt(x)
x |
an object of some kind. |
To satisfy the minimum requirements of an S3 class.
a boolean.
Steven E. Pav [email protected]
del_sropt
Other del_sropt:
as.del_sropt()
,
del_sropt
roll.own <- del_sropt(z.s=2,z.sub=1,df1=10,df2=1000,df1.sub=3,ope=1,epoch="yr") is.sropt(roll.own)
roll.own <- del_sropt(z.s=2,z.sub=1,df1=10,df2=1000,df1.sub=3,ope=1,epoch="yr") is.sropt(roll.own)
Checks if an object is in the class 'sr'
is.sr(x)
is.sr(x)
x |
an object of some kind. |
To satisfy the minimum requirements of an S3 class.
a boolean.
Steven E. Pav [email protected]
sr
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
rvs <- as.sr(rnorm(253*8),ope=253) is.sr(rvs)
rvs <- as.sr(rnorm(253*8),ope=253) is.sr(rvs)
Checks if an object is in the class 'sropt'
is.sropt(x)
is.sropt(x)
x |
an object of some kind. |
To satisfy the minimum requirements of an S3 class.
a boolean.
Steven E. Pav [email protected]
sropt
Other sropt:
as.sropt()
,
confint.sr()
,
dsropt()
,
pco_sropt()
,
power.sropt_test()
,
reannualize()
,
sropt_test()
,
sropt
nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) is.sropt(asro)
nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) is.sropt(asro)
Computes the variance covariance matrix of the inverse unified second moment matrix.
ism_vcov(X,vcov.func=vcov,fit.intercept=TRUE)
ism_vcov(X,vcov.func=vcov,fit.intercept=TRUE)
X |
an |
vcov.func |
a function which takes an object of class |
fit.intercept |
a boolean controlling whether we add a column of ones to the data, or fit the raw uncentered second moment. |
Given -vector
with mean
and
covariance,
, let
be
with a one prepended. Then let
,
the uncentered second moment matrix. The inverse of
contains the (negative) Markowitz portfolio
and the precision matrix.
Given contemporaneous observations of
-vectors,
stacked as rows in the
matrix
,
this function estimates the mean and the asymptotic
variance-covariance matrix of
.
One may use the default method for computing covariance,
via the vcov
function, or via a 'fancy' estimator,
like sandwich:vcovHAC
, sandwich:vcovHC
, etc.
a list containing the following components:
mu |
a |
Ohat |
the |
n |
the number of rows in |
p |
the number of assets. |
By flipping the sign of , the inverse of
contains the positive Markowitz
portfolio and the precision matrix on
. Performing
this transform before passing the data to this function
should be considered idiomatic.
This function will be deprecated in future releases of this package. Users should migrate at that time to a similar function in the MarkowitzR package.
Steven E. Pav [email protected]
Pav, S. E. "Asymptotic Distribution of the Markowitz Portfolio." 2013 https://arxiv.org/abs/1312.0557
X <- matrix(rnorm(1000*3),ncol=3) # putting in -X is idiomatic: ism <- ism_vcov(-X) iSigmas.n <- ism_vcov(-X,vcov.func="normal") iSigmas.n <- ism_vcov(-X,fit.intercept=FALSE) # compute the marginal Wald test statistics: ism.mu <- ism$mu[1:ism$p] ism.Sg <- ism$Ohat[1:ism$p,1:ism$p] wald.stats <- ism.mu / sqrt(diag(ism.Sg)) # make it fat tailed: X <- matrix(rt(1000*3,df=5),ncol=3) ism <- ism_vcov(X) wald.stats <- ism$mu[1:ism$p] / sqrt(diag(ism$Ohat[1:ism$p,1:ism$p])) if (require(sandwich)) { ism <- ism_vcov(X,vcov.func=vcovHC) wald.stats <- ism$mu[1:ism$p] / sqrt(diag(ism$Ohat[1:ism$p,1:ism$p])) } # add some autocorrelation to X Xf <- filter(X,c(0.2),"recursive") colnames(Xf) <- colnames(X) ism <- ism_vcov(Xf) wald.stats <- ism$mu[1:ism$p] / sqrt(diag(ism$Ohat[1:ism$p,1:ism$p])) if (require(sandwich)) { ism <- ism_vcov(Xf,vcov.func=vcovHAC) wald.stats <- ism$mu[1:ism$p] / sqrt(diag(ism$Ohat[1:ism$p,1:ism$p])) }
X <- matrix(rnorm(1000*3),ncol=3) # putting in -X is idiomatic: ism <- ism_vcov(-X) iSigmas.n <- ism_vcov(-X,vcov.func="normal") iSigmas.n <- ism_vcov(-X,fit.intercept=FALSE) # compute the marginal Wald test statistics: ism.mu <- ism$mu[1:ism$p] ism.Sg <- ism$Ohat[1:ism$p,1:ism$p] wald.stats <- ism.mu / sqrt(diag(ism.Sg)) # make it fat tailed: X <- matrix(rt(1000*3,df=5),ncol=3) ism <- ism_vcov(X) wald.stats <- ism$mu[1:ism$p] / sqrt(diag(ism$Ohat[1:ism$p,1:ism$p])) if (require(sandwich)) { ism <- ism_vcov(X,vcov.func=vcovHC) wald.stats <- ism$mu[1:ism$p] / sqrt(diag(ism$Ohat[1:ism$p,1:ism$p])) } # add some autocorrelation to X Xf <- filter(X,c(0.2),"recursive") colnames(Xf) <- colnames(X) ism <- ism_vcov(Xf) wald.stats <- ism$mu[1:ism$p] / sqrt(diag(ism$Ohat[1:ism$p,1:ism$p])) if (require(sandwich)) { ism <- ism_vcov(Xf,vcov.func=vcovHAC) wald.stats <- ism$mu[1:ism$p] / sqrt(diag(ism$Ohat[1:ism$p,1:ism$p])) }
Distribution function and quantile function for the 'confidence
distribution' of the maximal Sharpe ratio. This is just an inversion
to perform inference on given observed statistic
.
pco_sropt(q,df1,df2,z.s,ope,lower.tail=TRUE,log.p=FALSE) qco_sropt(p,df1,df2,z.s,ope,lower.tail=TRUE,log.p=FALSE,lb=0,ub=Inf)
pco_sropt(q,df1,df2,z.s,ope,lower.tail=TRUE,log.p=FALSE) qco_sropt(p,df1,df2,z.s,ope,lower.tail=TRUE,log.p=FALSE,lb=0,ub=Inf)
q |
vector of quantiles. |
df1 |
the number of assets in the portfolio. |
df2 |
the number of observations. |
z.s |
an observed Sharpe ratio statistic, annualized. |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
lower.tail |
logical; if TRUE (default), probabilities are
|
log.p |
logical; if TRUE, probabilities p are given as |
p |
vector of probabilities. |
lb |
the lower bound for the output of |
ub |
the upper bound for the output of |
Suppose follows a Maximal Sharpe ratio distribution
(see
SharpeR-package
) for known degrees of freedom, and
unknown non-centrality parameter . The
'confidence distribution' views
as a random
quantity once
is observed. As such, the CDF of
the confidence distribution is the same as that of the
Maximal Sharpe ratio (up to a flip of
lower.tail
);
while the quantile function is used to compute confidence
intervals on given
.
pco_sropt
gives the distribution function, and
qco_sropt
gives the quantile function.
Invalid arguments will result in return value NaN
with a warning.
When lower.tail
is true, pco_sropt
is monotonic increasing
with respect to q
, and decreasing in sropt
; these are reversed
when lower.tail
is false. Similarly, qco_sropt
is increasing
in sign(as.double(lower.tail) - 0.5) * p
and
- sign(as.double(lower.tail) - 0.5) * sropt
.
Steven E. Pav [email protected]
Other sropt:
as.sropt()
,
confint.sr()
,
dsropt()
,
is.sropt()
,
power.sropt_test()
,
reannualize()
,
sropt_test()
,
sropt
zeta.s <- 2.0 ope <- 253 ntest <- 50 df1 <- 4 df2 <- 6 * ope rvs <- rsropt(ntest,df1=df1,df2=df2,zeta.s=zeta.s) qvs <- seq(0,10,length.out=51) pps <- pco_sropt(qvs,df1,df2,rvs[1],ope) if (require(txtplot)) txtplot(qvs,pps) pps <- pco_sropt(qvs,df1,df2,rvs[1],ope,lower.tail=FALSE) if (require(txtplot)) txtplot(qvs,pps) svs <- seq(0,4,length.out=51) pps <- pco_sropt(2,df1,df2,svs,ope) pps <- pco_sropt(2,df1,df2,svs,ope,lower.tail=FALSE) pps <- pco_sropt(qvs,df1,df2,rvs[1],ope,lower.tail=FALSE) pco_sropt(-1,df1,df2,rvs[1],ope) qvs <- qco_sropt(0.05,df1=df1,df2=df2,z.s=rvs) mean(qvs > zeta.s) qvs <- qco_sropt(0.5,df1=df1,df2=df2,z.s=rvs) mean(qvs > zeta.s) qvs <- qco_sropt(0.95,df1=df1,df2=df2,z.s=rvs) mean(qvs > zeta.s) # test vectorization: qv <- qco_sropt(0.1,df1,df2,rvs) qv <- qco_sropt(c(0.1,0.2),df1,df2,rvs) qv <- qco_sropt(c(0.1,0.2),c(df1,2*df1),df2,rvs) qv <- qco_sropt(c(0.1,0.2),c(df1,2*df1),c(df2,2*df2),rvs)
zeta.s <- 2.0 ope <- 253 ntest <- 50 df1 <- 4 df2 <- 6 * ope rvs <- rsropt(ntest,df1=df1,df2=df2,zeta.s=zeta.s) qvs <- seq(0,10,length.out=51) pps <- pco_sropt(qvs,df1,df2,rvs[1],ope) if (require(txtplot)) txtplot(qvs,pps) pps <- pco_sropt(qvs,df1,df2,rvs[1],ope,lower.tail=FALSE) if (require(txtplot)) txtplot(qvs,pps) svs <- seq(0,4,length.out=51) pps <- pco_sropt(2,df1,df2,svs,ope) pps <- pco_sropt(2,df1,df2,svs,ope,lower.tail=FALSE) pps <- pco_sropt(qvs,df1,df2,rvs[1],ope,lower.tail=FALSE) pco_sropt(-1,df1,df2,rvs[1],ope) qvs <- qco_sropt(0.05,df1=df1,df2=df2,z.s=rvs) mean(qvs > zeta.s) qvs <- qco_sropt(0.5,df1=df1,df2=df2,z.s=rvs) mean(qvs > zeta.s) qvs <- qco_sropt(0.95,df1=df1,df2=df2,z.s=rvs) mean(qvs > zeta.s) # test vectorization: qv <- qco_sropt(0.1,df1,df2,rvs) qv <- qco_sropt(c(0.1,0.2),df1,df2,rvs) qv <- qco_sropt(c(0.1,0.2),c(df1,2*df1),df2,rvs) qv <- qco_sropt(c(0.1,0.2),c(df1,2*df1),c(df2,2*df2),rvs)
Distribution function and quantile function for LeCoutre's
lambda-prime distribution with df
degrees of freedom
and the observed t-statistic, tstat
.
plambdap(q, df, tstat, lower.tail = TRUE, log.p = FALSE) qlambdap(p, df, tstat, lower.tail = TRUE, log.p = FALSE) rlambdap(n, df, tstat)
plambdap(q, df, tstat, lower.tail = TRUE, log.p = FALSE) qlambdap(p, df, tstat, lower.tail = TRUE, log.p = FALSE) rlambdap(n, df, tstat)
q |
vector of quantiles. |
df |
the degrees of freedom of the t-statistic. |
tstat |
the observed (non-central) t-statistic. |
lower.tail |
logical; if TRUE (default), probabilities are
|
log.p |
logical; if TRUE, probabilities p are given as |
p |
vector of probabilities. |
n |
number of observations. If 'length(n) > 1', the length is taken to be the number required. |
Let be distributed
as a non-central t with
degrees of freedom and non-centrality
parameter
. We can view this as
where is a standard normal,
is the
non-centrality parameter,
is a chi-square RV with
degrees of freedom, independent of
. We can rewrite this as
Thus a 'lambda-prime' random variable with parameters and
is one expressable as a sum
for Chi-square with
d.f., independent from
standard normal
See ‘The Sharpe Ratio: Statistics and Applications’, section 2.4.
dlambdap
gives the density, plambdap
gives the distribution function,
qlambdap
gives the quantile function, and rlambdap
generates random deviates.
Invalid arguments will result in return value NaN
with a warning.
plambdap
should be an increasing function of the argument q
,
and decreasing in tstat
. qlambdap
should be increasing
in p
Steven E. Pav [email protected]
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
Lecoutre, Bruno. "Another look at confidence intervals for the noncentral t distribution." Journal of Modern Applied Statistical Methods 6, no. 1 (2007): 107–116. https://eris62.eu/telechargements/Lecoutre_Another_look-JMSAM2007_6(1).pdf
Lecoutre, Bruno. "Two useful distributions for Bayesian predictive procedures under normal models." Journal of Statistical Planning and Inference 79 (1999): 93–105.
t-distribution functions, dt,pt,qt,rt
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
rvs <- rnorm(128) pvs <- plambdap(rvs, 253*6, 0.5) plot(ecdf(pvs)) pvs <- plambdap(rvs, 253*6, 1) plot(ecdf(pvs)) pvs <- plambdap(rvs, 253*6, -0.5) plot(ecdf(pvs)) # test vectorization: qv <- qlambdap(0.1,128,2) qv <- qlambdap(c(0.1),128,2) qv <- qlambdap(c(0.2),128,2) qv <- qlambdap(c(0.2),253,2) qv <- qlambdap(c(0.1,0.2),128,2) qv <- qlambdap(c(0.1,0.2),c(128,253),2) qv <- qlambdap(c(0.1,0.2),c(128,253),c(2,4)) qv <- qlambdap(c(0.1,0.2),c(128,253),c(2,4,8,16)) # random generation rv <- rlambdap(1000,252,2)
rvs <- rnorm(128) pvs <- plambdap(rvs, 253*6, 0.5) plot(ecdf(pvs)) pvs <- plambdap(rvs, 253*6, 1) plot(ecdf(pvs)) pvs <- plambdap(rvs, 253*6, -0.5) plot(ecdf(pvs)) # test vectorization: qv <- qlambdap(0.1,128,2) qv <- qlambdap(c(0.1),128,2) qv <- qlambdap(c(0.2),128,2) qv <- qlambdap(c(0.2),253,2) qv <- qlambdap(c(0.1,0.2),128,2) qv <- qlambdap(c(0.1,0.2),c(128,253),2) qv <- qlambdap(c(0.1,0.2),c(128,253),c(2,4)) qv <- qlambdap(c(0.1,0.2),c(128,253),c(2,4,8,16)) # random generation rv <- rlambdap(1000,252,2)
Compute power of test, or determine parameters to obtain target power.
power.sr_test(n=NULL,zeta=NULL,sig.level=0.05,power=NULL, alternative=c("one.sided","two.sided"),ope=NULL)
power.sr_test(n=NULL,zeta=NULL,sig.level=0.05,power=NULL, alternative=c("one.sided","two.sided"),ope=NULL)
n |
Number of observations |
zeta |
the 'signal-to-noise' parameter, defined as the population mean divided by the population standard deviation, 'annualized'. |
sig.level |
Significance level (Type I error probability). |
power |
Power of test (1 minus Type II error probability). |
alternative |
One- or two-sided test. |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
Suppose you perform a single-sample test for significance of the
Sharpe ratio based on the corresponding single-sample t-test.
Given any three of: the effect size (the population SNR, ),
the number of observations, and the type I and type II rates,
this function computes the fourth.
See ‘The Sharpe Ratio: Statistics and Applications’, section 2.5.8.
This is a thin wrapper on power.t.test
.
Exactly one of the parameters n
, zeta
, power
, and
sig.level
must be passed as NULL, and that parameter is determined
from the others. Notice that sig.level
has non-NULL default, so NULL
must be explicitly passed if you want to compute it.
Object of class power.htest
, a list of the arguments
(including the computed one) augmented with method
, note
and n.epoch
elements, the latter is the number of epochs
under the given annualization (ope
), NA
if none given.
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Johnson, N. L., and Welch, B. L. "Applications of the non-central t-distribution." Biometrika 31, no. 3-4 (1940): 362-389. doi:10.1093/biomet/31.3-4.362
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
Lehr, R. "Sixteen S-squared over D-squared: A relation for crude sample size estimates." Statist. Med., 11, no 8 (1992): 1099–1102. doi:10.1002/sim.4780110811
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
anex <- power.sr_test(253,1,0.05,NULL,ope=253) anex <- power.sr_test(n=253,zeta=NULL,sig.level=0.05,power=0.5,ope=253) anex <- power.sr_test(n=NULL,zeta=0.6,sig.level=0.05,power=0.5,ope=253) # Lehr's Rule zetas <- seq(0.1,2.5,length.out=51) ssizes <- sapply(zetas,function(zed) { x <- power.sr_test(n=NULL,zeta=zed,sig.level=0.05,power=0.8, alternative="two.sided",ope=253) x$n / 253}) # should be around 8. print(summary(ssizes * zetas * zetas)) # e = n z^2 mnemonic approximate rule for 0.05 type I, 50% power ssizes <- sapply(zetas,function(zed) { x <- power.sr_test(n=NULL,zeta=zed,sig.level=0.05,power=0.5,ope=253) x$n / 253 }) print(summary(ssizes * zetas * zetas - exp(1)))
anex <- power.sr_test(253,1,0.05,NULL,ope=253) anex <- power.sr_test(n=253,zeta=NULL,sig.level=0.05,power=0.5,ope=253) anex <- power.sr_test(n=NULL,zeta=0.6,sig.level=0.05,power=0.5,ope=253) # Lehr's Rule zetas <- seq(0.1,2.5,length.out=51) ssizes <- sapply(zetas,function(zed) { x <- power.sr_test(n=NULL,zeta=zed,sig.level=0.05,power=0.8, alternative="two.sided",ope=253) x$n / 253}) # should be around 8. print(summary(ssizes * zetas * zetas)) # e = n z^2 mnemonic approximate rule for 0.05 type I, 50% power ssizes <- sapply(zetas,function(zed) { x <- power.sr_test(n=NULL,zeta=zed,sig.level=0.05,power=0.5,ope=253) x$n / 253 }) print(summary(ssizes * zetas * zetas - exp(1)))
Compute power of test, or determine parameters to obtain target power.
power.sropt_test(df1=NULL,df2=NULL,zeta.s=NULL, sig.level=0.05,power=NULL,ope=1)
power.sropt_test(df1=NULL,df2=NULL,zeta.s=NULL, sig.level=0.05,power=NULL,ope=1)
df1 |
the number of assets in the portfolio. |
df2 |
the number of observations. |
zeta.s |
the 'signal-to-noise' parameter, defined as ... |
sig.level |
Significance level (Type I error probability). |
power |
Power of test (1 minus Type II error probability). |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
Suppose you perform a single-sample test for significance of the
optimal Sharpe ratio based on the corresponding single-sample T^2-test.
Given any four of: the effect size (the population optimal SNR,
), the number of assets, the number of observations,
and the type I and type II rates, this function computes the fifth.
See ‘The Sharpe Ratio: Statistics and Applications’, section 6.3.3.
Exactly one of the parameters df1
, df2
,
zeta.s
, power
, and
sig.level
must be passed as NULL, and that parameter is determined
from the others. Notice that sig.level
has non-NULL default, so NULL
must be explicitly passed if you want to compute it.
Object of class power.htest
, a list of the arguments
(including the computed one) augmented with method
, note
and n.epoch
elements, the latter is the number of epochs
under the given annualization (ope
), NA
if none given.
Steven E. Pav [email protected]
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
Other sropt:
as.sropt()
,
confint.sr()
,
dsropt()
,
is.sropt()
,
pco_sropt()
,
reannualize()
,
sropt_test()
,
sropt
anex <- power.sropt_test(8,4*253,1,0.05,NULL,ope=253)
anex <- power.sropt_test(8,4*253,1,0.05,NULL,ope=253)
Computes the prediction interval for Sharpe ratio.
predint( x, oosdf, oosrescal = 1/sqrt(oosdf + 1), ope = NULL, level = 0.95, level.lo = (1 - level)/2, level.hi = 1 - level.lo, type = c("t", "Z", "Mertens", "Bao") )
predint( x, oosdf, oosrescal = 1/sqrt(oosdf + 1), ope = NULL, level = 0.95, level.lo = (1 - level)/2, level.hi = 1 - level.lo, type = c("t", "Z", "Mertens", "Bao") )
x |
a (non-empty) numeric vector of data values, or an
object of class |
oosdf |
the future (or 'out of sample', thus 'oos') degrees of freedom. In the vanilla Sharpe case, this is the number of future observations minus one. |
oosrescal |
the rescaling parameter for the future Sharpe ratio. The default value holds for the case of unattributed models ('vanilla Shape'), but can be set to some other value to deal with the magnitude of attribution factors in the future period. |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
level |
the confidence level required. |
level.lo |
the lower confidence level required. |
level.hi |
the upper confidence level required. |
type |
which method to apply. Only methods based on an approximate standard error are supported. |
Given observations
from a normal random variable,
with mean
and standard deviation
, computes
an interval
such that with a fixed probability,
the sample Sharpe ratio over
future observations will fall in the
given interval. The coverage is over repeated draws of both the past and
future data, thus this computation takes into account error in both the
estimate of Sharpe and the as yet unrealized returns.
Coverage is approximate. Prediction intervals are computed by
inflating a confidence interval by an amount which depends on the sample
sizes.
See ‘The Sharpe Ratio: Statistics and Applications’, sections 2.5.9 and 3.5.2.
A matrix (or vector) with columns giving lower and upper
confidence limits for the parameter. These will be labelled as
level.lo and level.hi in %, e.g. "2.5 %"
if level.lo < 0
or level.hi > 1
, NaN
will be
returned.
Steven E. Pav [email protected]
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
# should reject null set.seed(1234) etc <- predint(rnorm(1000,mean=0.5,sd=0.1),oosdf=127,ope=1) etc <- predint(matrix(rnorm(1000*5,mean=0.05),ncol=5),oosdf=63,ope=1) # check coverage mu <- 0.0005 sg <- 0.013 n1 <- 512 n2 <- 256 p <- 100 x1 <- matrix(rnorm(n1*p,mean=mu,sd=sg),ncol=p) x2 <- matrix(rnorm(n2*p,mean=mu,sd=sg),ncol=p) sr1 <- as.sr(x1) sr2 <- as.sr(x2) # check coverage of prediction interval etc1 <- predint(sr1,oosdf=n2-1,level=0.95) is.ok <- (etc1[,1] <= sr2$sr) & (sr2$sr <= etc1[,2]) covr <- mean(is.ok)
# should reject null set.seed(1234) etc <- predint(rnorm(1000,mean=0.5,sd=0.1),oosdf=127,ope=1) etc <- predint(matrix(rnorm(1000*5,mean=0.05),ncol=5),oosdf=63,ope=1) # check coverage mu <- 0.0005 sg <- 0.013 n1 <- 512 n2 <- 256 p <- 100 x1 <- matrix(rnorm(n1*p,mean=mu,sd=sg),ncol=p) x2 <- matrix(rnorm(n2*p,mean=mu,sd=sg),ncol=p) sr1 <- as.sr(x1) sr2 <- as.sr(x2) # check coverage of prediction interval etc1 <- predint(sr1,oosdf=n2-1,level=0.95) is.ok <- (etc1[,1] <= sr2$sr) & (sr2$sr <= etc1[,2]) covr <- mean(is.ok)
Displays an object, returning it invisibly,
(via invisible(x)
.)
## S3 method for class 'sr' print(x, ...) ## S3 method for class 'sropt' print(x, ...) ## S3 method for class 'del_sropt' print(x, ...)
## S3 method for class 'sr' print(x, ...) ## S3 method for class 'sropt' print(x, ...) ## S3 method for class 'del_sropt' print(x, ...)
x |
an object of class |
... |
further arguments to be passed to or from methods. |
the object, wrapped in invisible
.
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
# compute a 'daily' Sharpe mysr <- as.sr(rnorm(253*8),ope=1,epoch="day") print(mysr) # roll your own. ope <- 253 zeta <- 1.0 n <- 6 * ope rvs <- rsr(1,n,zeta,ope=ope) roll.own <- sr(sr=rvs,df=n-1,ope=ope,rescal=sqrt(1/n)) print(roll.own) # put a bunch in. naming becomes a problem. rvs <- rsr(5,n,zeta,ope=ope) roll.own <- sr(sr=rvs,df=n-1,ope=ope,rescal=sqrt(1/n)) print(roll.own) # for sropt objects: nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) print(asro)
# compute a 'daily' Sharpe mysr <- as.sr(rnorm(253*8),ope=1,epoch="day") print(mysr) # roll your own. ope <- 253 zeta <- 1.0 n <- 6 * ope rvs <- rsr(1,n,zeta,ope=ope) roll.own <- sr(sr=rvs,df=n-1,ope=ope,rescal=sqrt(1/n)) print(roll.own) # put a bunch in. naming becomes a problem. rvs <- rsr(5,n,zeta,ope=ope) roll.own <- sr(sr=rvs,df=n-1,ope=ope,rescal=sqrt(1/n)) print(roll.own) # for sropt objects: nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) print(asro)
Changes the annualization factor of a Sharpe ratio statistic, or the rate at which observations are made.
reannualize(object, new.ope = NULL, new.epoch = NULL) ## S3 method for class 'sr' reannualize(object, new.ope = NULL, new.epoch = NULL) ## S3 method for class 'sropt' reannualize(object, new.ope = NULL, new.epoch = NULL)
reannualize(object, new.ope = NULL, new.epoch = NULL) ## S3 method for class 'sr' reannualize(object, new.ope = NULL, new.epoch = NULL) ## S3 method for class 'sropt' reannualize(object, new.ope = NULL, new.epoch = NULL)
object |
an object of class |
new.ope |
the new observations per epoch. If none given, it is not updated. |
new.epoch |
a string representation of the epoch. If none given, it is not updated. |
the input object with the annualization and/or epoch updated.
Steven E. Pav [email protected]
sr
sropt
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
Other sropt:
as.sropt()
,
confint.sr()
,
dsropt()
,
is.sropt()
,
pco_sropt()
,
power.sropt_test()
,
sropt_test()
,
sropt
# compute a 'daily' Sharpe mysr <- as.sr(rnorm(253*8),ope=1,epoch="day") # turn into annual mysr2 <- reannualize(mysr,new.ope=253,new.epoch="yr") # for sropt ope <- 253 zeta.s <- 1.0 df1 <- 10 df2 <- 6 * ope rvs <- rsropt(1,df1,df2,zeta.s,ope,drag=0) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope,epoch="yr") # make 'monthly' roll.monthly <- reannualize(roll.own,new.ope=21,new.epoch="mo.") # make 'daily' roll.daily <- reannualize(roll.own,new.ope=1,new.epoch="day")
# compute a 'daily' Sharpe mysr <- as.sr(rnorm(253*8),ope=1,epoch="day") # turn into annual mysr2 <- reannualize(mysr,new.ope=253,new.epoch="yr") # for sropt ope <- 253 zeta.s <- 1.0 df1 <- 10 df2 <- 6 * ope rvs <- rsropt(1,df1,df2,zeta.s,ope,drag=0) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope,epoch="yr") # make 'monthly' roll.monthly <- reannualize(roll.own,new.ope=21,new.epoch="mo.") # make 'daily' roll.daily <- reannualize(roll.own,new.ope=1,new.epoch="day")
Estimates the standard error of the Sharpe ratio statistic.
se(z, type) ## S3 method for class 'sr' se(z, type = c("t", "Lo", "Mertens", "Bao"))
se(z, type) ## S3 method for class 'sr' se(z, type = c("t", "Lo", "Mertens", "Bao"))
z |
an observed Sharpe ratio statistic, of class |
type |
estimator type. one of |
For an observed Sharpe ratio, estimate the standard error. The following methods are recognized:
The default, based on Johnson & Welch, with a correction
for small sample size. Also known as 'Lo'
.
An approximation to the standard error taking into skewness and kurtosis of the returns distribution.
An even higher accuracty approximation using higher order moments.
There should be very little difference between these except for very small sample sizes.
See ‘The Sharpe Ratio: Statistics and Applications’, sections 2.5.1 and 3.2.3.
an estimate of standard error.
The units of the standard error are consistent with those of the
input sr
object.
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Johnson, N. L., and Welch, B. L. "Applications of the non-central t-distribution." Biometrika 31, no. 3-4 (1940): 362-389. doi:10.1093/biomet/31.3-4.362
Lo, Andrew W. "The statistics of Sharpe ratios." Financial Analysts Journal 58, no. 4 (2002): 36-52. https://www.ssrn.com/paper=377260
Bao, Yong. "Estimation Risk-Adjusted Sharpe Ratio and Fund Performance Ranking Under a General Return Distribution." Journal of Financial Econometrics 7, no. 2 (2009): 152-173. doi:10.1093/jjfinec/nbn022
Opdyke, J. D. "Comparing Sharpe Ratios: So Where are the p-values?" Journal of Asset Management 8, no. 5 (2006): 308-336. https://www.ssrn.com/paper=886728
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
Walck, C. "Hand-book on STATISTICAL DISTRIBUTIONS for experimentalists." 1996. http://www.stat.rice.edu/~dobelman/textfiles/DistributionsHandbook.pdf
sr-distribution functions, dsr
,
sr_variance
.
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
asr <- as.sr(rnorm(128,0.2)) anse <- se(asr,type="t") anse <- se(asr,type="Lo")
asr <- as.sr(rnorm(128,0.2)) anse <- se(asr,type="t") anse <- se(asr,type="Lo")
News for package 'SharpeR'
Remove tests based on upsilon distribution. Also removes dependency on sadists package.
CRAN fix for warnings about ellipsis.
move github figures to location CRAN understands
be smarter about S3 classes: do not redefine summary and print.
add bias and variance from Bao (2009).
support estimation of higher order moments in as.sr
,
and expands methods for se and confidence interval computations.
incorporate higher order methods into one sample sr tests.
fix sr_vcov on array input.
add SRIC method.
add SRIC to print.sropt.
change predint output to matrix.
sane version numbers.
unpaired k sample test of Sharpe.
rely on same for unpaired 2 sample test.
prediction intervals for Sharpe based on upsilon.
more tests.
fix inference of mark frequency from e.g. xts objects.
add rlambdap.
fix second moment asymptotic covariance.
add confidence distribution functions for sr, sr.opt.
inverse second moment asymptotic covariance.
spanning/hedging tests.
sr equality test via callback variance covariance computation.
split vignette in two.
proper d.f. in sr objects with different nan fill.
restore vignette.
put on CRAN
Computes the variance covariance matrix of sample mean and second moment.
sm_vcov(X,vcov.func=vcov,fit.intercept=TRUE)
sm_vcov(X,vcov.func=vcov,fit.intercept=TRUE)
X |
an |
vcov.func |
a function which takes an object of class |
fit.intercept |
a boolean controlling whether we add a column of ones to the data, or fit the raw uncentered second moment. |
Given -vector
, the 'unified' sample is the
vector of
stacked on top
of
.
Given
contemporaneous observations of
-vectors,
stacked as rows in the
matrix
,
this function computes the mean and the variance-covariance
matrix of the 'unified' sample.
One may use the default method for computing covariance,
via the vcov
function, or via a 'fancy' estimator,
like sandwich:vcovHAC
, sandwich:vcovHC
, etc.
a list containing the following components:
mu |
a |
Ohat |
the |
n |
the number of rows in |
p |
the number of assets. |
This function will be deprecated in future releases of this package. Users should migrate at that time to a similar function in the MarkowitzR package.
Steven E. Pav [email protected]
Pav, S. E. "Asymptotic Distribution of the Markowitz Portfolio." 2013 https://arxiv.org/abs/1312.0557
X <- matrix(rnorm(1000*3),ncol=3) Sigmas <- sm_vcov(X) Sigmas.n <- sm_vcov(X,vcov.func="normal") Sigmas.n <- sm_vcov(X,fit.intercept=FALSE) # make it fat tailed: X <- matrix(rt(1000*3,df=5),ncol=3) Sigmas <- sm_vcov(X) if (require(sandwich)) { Sigmas <- sm_vcov(X,vcov.func=vcovHC) } # add some autocorrelation to X Xf <- filter(X,c(0.2),"recursive") colnames(Xf) <- colnames(X) Sigmas <- sm_vcov(Xf) if (require(sandwich)) { Sigmas <- sm_vcov(Xf,vcov.func=vcovHAC) }
X <- matrix(rnorm(1000*3),ncol=3) Sigmas <- sm_vcov(X) Sigmas.n <- sm_vcov(X,vcov.func="normal") Sigmas.n <- sm_vcov(X,fit.intercept=FALSE) # make it fat tailed: X <- matrix(rt(1000*3,df=5),ncol=3) Sigmas <- sm_vcov(X) if (require(sandwich)) { Sigmas <- sm_vcov(X,vcov.func=vcovHC) } # add some autocorrelation to X Xf <- filter(X,c(0.2),"recursive") colnames(Xf) <- colnames(X) Sigmas <- sm_vcov(Xf) if (require(sandwich)) { Sigmas <- sm_vcov(Xf,vcov.func=vcovHAC) }
Spawns an object of class sr
.
sr( sr, df, c0 = 0, ope = 1, rescal = sqrt(1/(df + 1)), epoch = "yr", cumulants = NULL )
sr( sr, df, c0 = 0, ope = 1, rescal = sqrt(1/(df + 1)), epoch = "yr", cumulants = NULL )
sr |
a Sharpe ratio statistic. |
df |
the degrees of freedom of the equivalent t-statistic. |
c0 |
the 'risk-free' or 'disastrous' rate of return. this is assumed to be given in the same units as x, not in 'annualized' terms. |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
rescal |
the rescaling parameter. |
epoch |
the string representation of the 'epoch', defaulting to 'yr'. |
cumulants |
an optional array of the higher order cumulants of the returns distribution. The first element shall be the skew; the second the excess kurtosis. Up to the sixth cumulant can be given. Higher order approximations for the moments of the Sharpe ratio can be computed based on these cumulants. |
The sr
class contains information about a rescaled t-statistic.
The following are list attributes of the object:
The Sharpe ratio statistic.
The d.f. of the equivalent t-statistic.
The drag 'risk free rate' used.
The 'observations per epoch'.
The rescaling parameter.
The string name of the 'epoch'.
The stored Sharpe statistic, sr
is equal to the t-statistic
times .
For the most part, this constructor should not be called directly,
rather as.sr
should be called instead to compute the
Sharpe ratio.
a list cast to class sr
.
2FIX: allow rownames?
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
summary.sr
# roll your own. ope <- 253 zeta <- 1.0 n <- 3 * ope rvs <- rsr(1,n,zeta,ope=ope) roll.own <- sr(sr=rvs,df=n-1,ope=ope,rescal=sqrt(1/n)) # put a bunch in. naming becomes a problem. rvs <- rsr(5,n,zeta,ope=ope) roll.own <- sr(sr=rvs,df=n-1,ope=ope,rescal=sqrt(1/n))
# roll your own. ope <- 253 zeta <- 1.0 n <- 3 * ope rvs <- rsr(1,n,zeta,ope=ope) roll.own <- sr(sr=rvs,df=n-1,ope=ope,rescal=sqrt(1/n)) # put a bunch in. naming becomes a problem. rvs <- rsr(5,n,zeta,ope=ope) roll.own <- sr(sr=rvs,df=n-1,ope=ope,rescal=sqrt(1/n))
Computes the asymptotic bias of the sample Sharpe ratio based on moments.
sr_bias(snr, n, cumulants, type = c("simple", "second_order"))
sr_bias(snr, n, cumulants, type = c("simple", "second_order"))
snr |
the population Signal Noise ratio. Often one will use the population estimate instead. |
n |
the sample size that the Shapre ratio is observed on. |
cumulants |
a vector of the third through fourth, or the third through seventh population cumulants of the random variable. More terms are needed for the higher accuracy approximation. |
type |
determines the order of accuracy of the bias approximation. Takes values of
|
The sample Sharpe ratio has bias of the form
where is the population Signal Noise ratio,
is the sample size,
is the population skewness,
and
is the population excess kurtosis.
This form of the bias appears as Equation (5) in Bao, which
claims an accuracy of only
. The
author believes this approximation is slightly more accurate.
A more accurate form is given by Bao (Equation (3)) as
where through
are the fifth through
seventh cumulants of the error term.
See ‘The Sharpe Ratio: Statistics and Applications’, section 3.2.3.
the approximate bias of the Sharpe ratio. The bias is the expected value of the sample Sharpe minus the Signal Noise ratio.
much of the code is adapted from Gauss code provided by Yong Bao.
Steven E. Pav [email protected]
Bao, Yong. "Estimation Risk-Adjusted Sharpe Ratio and Fund Performance Ranking Under a General Return Distribution." Journal of Financial Econometrics 7, no. 2 (2009): 152-173. doi:10.1093/jjfinec/nbn022
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
# bias under normality: sr_bias(1, 100, rep(0,2), type='simple') sr_bias(1, 100, rep(0,5), type='second_order') # plugging in sample estimates x <- rnorm(1000) n <- length(x) mu <- mean(x) sdv <- sd(x) snr <- mu / sdv # these are not great estimates, but close enough: sku <- mean((x-mu)^3) / sdv^3 kur <- (mean((x-mu)^4) / sdv^4) - 4 sr_bias(snr, n, c(sku,kur), type='simple')
# bias under normality: sr_bias(1, 100, rep(0,2), type='simple') sr_bias(1, 100, rep(0,5), type='second_order') # plugging in sample estimates x <- rnorm(1000) n <- length(x) mu <- mean(x) sdv <- sd(x) snr <- mu / sdv # these are not great estimates, but close enough: sku <- mean((x-mu)^3) / sdv^3 kur <- (mean((x-mu)^4) / sdv^4) - 4 sr_bias(snr, n, c(sku,kur), type='simple')
Performs a hypothesis test of equality of Sharpe ratios of p assets given paired observations.
sr_equality_test(X,type=c("chisq","F","t"), alternative=c("two.sided","less","greater"), contrasts=NULL, vcov.func=vcov)
sr_equality_test(X,type=c("chisq","F","t"), alternative=c("two.sided","less","greater"), contrasts=NULL, vcov.func=vcov)
X |
an |
type |
which approximation to use. |
alternative |
a character string specifying the alternative hypothesis,
must be one of |
contrasts |
an |
vcov.func |
a function which takes a model of class lm (one of
the form x ~ 1), and produces a variance-covariance matrix.
The default is |
Given i.i.d. observations of the excess returns of
strategies, we test
using the method of Wright, et. al.
More generally, a matrix of constrasts, can be given, and we can
test
where is the vector of Sharpe ratios of the
strategies.
When consists of a single row (a single contrast), as is the
case when the default contrasts are used and only two strategies are
compared, then an approximate t-test can be performed against the
alternative hypothesis
Both chi-squared and F- approximations are supported; the former is described by Wright. et. al., the latter by Leung and Wong.
See ‘The Sharpe Ratio: Statistics and Applications’, section 3.3.1.
Object of class htest
, a list of the test statistic,
the size of X
, and the method
noted.
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Wright, J. A., Yam, S. C. P., and Yung, S. P. "A note on the test for the equality of multiple Sharpe ratios and its application on the evaluation of iShares." J. Risk. to appear. https://www.risk.net/journal-risk/2340067/test-equality-multiple-sharpe-ratios
Leung, P.-L., and Wong, W.-K. "On testing the equality of multiple Sharpe ratios, with application on the evaluation of iShares." J. Risk 10, no. 3 (2008): 15–30. https://papers.ssrn.com/sol3/papers.cfm?abstract_id=907270
Memmel, C. "Performance hypothesis testing with the Sharpe ratio." Finance Letters 1 (2003): 21–23.
Ledoit, O., and Wolf, M. "Robust performance hypothesis testing with the Sharpe ratio." Journal of Empirical Finance 15, no. 5 (2008): 850-859. doi:10.1016/j.jempfin.2008.03.002
Lo, Andrew W. "The statistics of Sharpe ratios." Financial Analysts Journal 58, no. 4 (2002): 36-52. https://www.ssrn.com/paper=377260
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
# under the null set.seed(1234) rv <- sr_equality_test(matrix(rnorm(500*5),ncol=5)) # under the alternative (but with identity covariance) ope <- 253 nyr <- 10 nco <- 5 set.seed(909) rets <- 0.01 * sapply(seq(0,1.7/sqrt(ope),length.out=nco), function(mu) { rnorm(ope*nyr,mean=mu,sd=1) }) rv <- sr_equality_test(rets) # using real data if (require(xts)) { data(stock_returns) pvs <- sr_equality_test(stock_returns) } # test for uniformity pvs <- replicate(1024,{ x <- sr_equality_test(matrix(rnorm(400*5),400,5),type="chisq") x$p.value }) plot(ecdf(pvs)) abline(0,1,col='red') if (require(sandwich)) { set.seed(as.integer(charToRaw("0b2fd4e9-3bdf-4e3e-9c75-25c6d18c331f"))) n.manifest <- 10 n.latent <- 4 n.day <- 1024 snr <- 0.95 la_A <- matrix(rnorm(n.day*n.latent),ncol=n.latent) la_B <- matrix(runif(n.latent*n.manifest),ncol=n.manifest) latent.rets <- la_A %*% la_B noise.rets <- matrix(rnorm(n.day*n.manifest),ncol=n.manifest) some.rets <- snr * latent.rets + sqrt(1-snr^2) * noise.rets # naive vcov pvs0 <- sr_equality_test(some.rets) # HAC vcov pvs1 <- sr_equality_test(some.rets,vcov.func=vcovHAC) # more elaborately: pvs <- sr_equality_test(some.rets,vcov.func=function(amod) { vcovHAC(amod,prewhite=TRUE) }) }
# under the null set.seed(1234) rv <- sr_equality_test(matrix(rnorm(500*5),ncol=5)) # under the alternative (but with identity covariance) ope <- 253 nyr <- 10 nco <- 5 set.seed(909) rets <- 0.01 * sapply(seq(0,1.7/sqrt(ope),length.out=nco), function(mu) { rnorm(ope*nyr,mean=mu,sd=1) }) rv <- sr_equality_test(rets) # using real data if (require(xts)) { data(stock_returns) pvs <- sr_equality_test(stock_returns) } # test for uniformity pvs <- replicate(1024,{ x <- sr_equality_test(matrix(rnorm(400*5),400,5),type="chisq") x$p.value }) plot(ecdf(pvs)) abline(0,1,col='red') if (require(sandwich)) { set.seed(as.integer(charToRaw("0b2fd4e9-3bdf-4e3e-9c75-25c6d18c331f"))) n.manifest <- 10 n.latent <- 4 n.day <- 1024 snr <- 0.95 la_A <- matrix(rnorm(n.day*n.latent),ncol=n.latent) la_B <- matrix(runif(n.latent*n.manifest),ncol=n.manifest) latent.rets <- la_A %*% la_B noise.rets <- matrix(rnorm(n.day*n.manifest),ncol=n.manifest) some.rets <- snr * latent.rets + sqrt(1-snr^2) * noise.rets # naive vcov pvs0 <- sr_equality_test(some.rets) # HAC vcov pvs1 <- sr_equality_test(some.rets,vcov.func=vcovHAC) # more elaborately: pvs <- sr_equality_test(some.rets,vcov.func=function(amod) { vcovHAC(amod,prewhite=TRUE) }) }
Performs one and two sample tests of Sharpe ratio on vectors of data.
sr_test( x, y = NULL, alternative = c("two.sided", "less", "greater"), zeta = 0, ope = 1, paired = FALSE, conf.level = 0.95, type = c("exact", "t", "Z", "Mertens", "Bao"), ... )
sr_test( x, y = NULL, alternative = c("two.sided", "less", "greater"), zeta = 0, ope = 1, paired = FALSE, conf.level = 0.95, type = c("exact", "t", "Z", "Mertens", "Bao"), ... )
x |
a (non-empty) numeric vector of data values, or an
object of class |
y |
an optional (non-empty) numeric vector of data values, or
an object of class |
alternative |
a character string specifying the alternative hypothesis,
must be one of |
zeta |
a number indicating the null hypothesis offset value, the
|
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
paired |
a logical indicating whether you want a paired test. |
conf.level |
confidence level of the interval. |
type |
which method to apply. |
... |
further arguments to be passed to or from methods. |
Given observations
from a normal random variable,
with mean
and standard deviation
, tests
against two or one sided alternatives.
Can also perform two sample tests of Sharpe ratio. For paired observations
and
, tests
against two or one sided alternative, via
sr_equality_test
.
For unpaired (and independent) observations, tests
against two or one-sided alternatives via an asymptotic approximation.
The one sample test admits a number of different methods:
The default, which is only exact when returns are normal, based on inverting the non-central t distribution.
Uses the Johnson Welch approximation to the standard error, centered around the sample value.
Uses the Johnson Welch approximation to the standard error, performing a simple correction for the bias of the Sharpe ratio based on Miller and Gehr formula.
Uses the Mertens higher order approximation to the standard error, centered around the sample value.
Uses the Bao higher order approximation to the standard error, performing a higher order correction for the bias of the Sharpe ratio.
See confint.sr
for more information on these types
See ‘The Sharpe Ratio: Statistics and Applications’, section 3.2.1, 3.2.2, and 3.3.1.
A list with class "htest"
containing the following components:
statistic |
the value of the t- or Z-statistic. |
parameter |
the degrees of freedom for the statistic. |
p.value |
the p-value for the test. |
conf.int |
a confidence interval appropriate to the specified alternative hypothesis. NYI for some cases. |
estimate |
the estimated Sharpe or difference in Sharpes depending on whether it was a one-sample test or a two-sample test. Annualized |
null.value |
the specified hypothesized value of the Sharpe or difference of Sharpes depending on whether it was a one-sample test or a two-sample test. |
alternative |
a character string describing the alternative hypothesis. |
method |
a character string indicating what type of test was performed. |
data.name |
a character string giving the name(s) of the data. |
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
sr_equality_test
, sr_unpaired_test
, t.test
.
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
,
summary.sr
# should reject null x <- sr_test(rnorm(1000,mean=0.5,sd=0.1),zeta=2,ope=1,alternative="greater") x <- sr_test(rnorm(1000,mean=0.5,sd=0.1),zeta=2,ope=1,alternative="two.sided") # should not reject null x <- sr_test(rnorm(1000,mean=0.5,sd=0.1),zeta=2,ope=1,alternative="less") # test for uniformity pvs <- replicate(128,{ x <- sr_test(rnorm(1000),ope=253,alternative="two.sided") x$p.value }) plot(ecdf(pvs)) abline(0,1,col='red') # testing an object of class sr asr <- as.sr(rnorm(1000,1 / sqrt(253)),ope=253) checkit <- sr_test(asr,zeta=0)
# should reject null x <- sr_test(rnorm(1000,mean=0.5,sd=0.1),zeta=2,ope=1,alternative="greater") x <- sr_test(rnorm(1000,mean=0.5,sd=0.1),zeta=2,ope=1,alternative="two.sided") # should not reject null x <- sr_test(rnorm(1000,mean=0.5,sd=0.1),zeta=2,ope=1,alternative="less") # test for uniformity pvs <- replicate(128,{ x <- sr_test(rnorm(1000),ope=253,alternative="two.sided") x$p.value }) plot(ecdf(pvs)) abline(0,1,col='red') # testing an object of class sr asr <- as.sr(rnorm(1000,1 / sqrt(253)),ope=253) checkit <- sr_test(asr,zeta=0)
Performs hypothesis tests on a single equation on k independent samples of Sharpe ratio.
sr_unpaired_test( srs, contrasts = NULL, null.value = 0, alternative = c("two.sided", "less", "greater"), ope = NULL, conf.level = 0.95 )
sr_unpaired_test( srs, contrasts = NULL, null.value = 0, alternative = c("two.sided", "less", "greater"), ope = NULL, conf.level = 0.95 )
srs |
a (non-empty) list of objects of class |
contrasts |
an array of the constrasts, the |
null.value |
the constant null value, the |
alternative |
a character string specifying the alternative hypothesis,
must be one of |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
conf.level |
confidence level of the interval. |
For , suppose you have
observations of a normal random variable with mean
and
standard deviation
, with all observations
independent. Given constants
and value
, this
code tests the null hypothesis
against two or one sided alternatives.
See ‘The Sharpe Ratio: Statistics and Applications’, section 3.3.1.
A list with class "htest"
containing the following components:
statistic |
The Wald statistic. |
parameter |
The degrees of freedom of the Wald statistic. |
p.value |
the p-value for the test. |
conf.int |
a confidence interval appropriate to the specified alternative hypothesis. |
estimate |
the estimated equation value, just the weighted sum of the sample Sharpe ratios. Annualized |
null.value |
the specified hypothesized value of the sum of Sharpes. |
alternative |
a character string describing the alternative hypothesis. |
method |
a character string indicating what type of test was performed. |
data.name |
a character string giving the name(s) of the data. |
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
sr_equality_test
, sr_test
, t.test
.
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_vcov()
,
sr
,
summary.sr
# basic usage set.seed(as.integer(charToRaw("set the seed"))) # default contrast is 1,-1,1,-1,1,-1 etc <- sr_unpaired_test(as.sr(matrix(rnorm(1000*6,mean=0.02,sd=0.1),ncol=6))) print(etc) etc <- sr_unpaired_test(as.sr(matrix(rnorm(1000*4,mean=0.0005,sd=0.01),ncol=4)), alternative='greater') print(etc) etc <- sr_unpaired_test(as.sr(matrix(rnorm(1000*4,mean=0.0005,sd=0.01),ncol=4)), contrasts=c(1,1,1,1),null.value=-0.1,alternative='greater') print(etc) inp <- list(as.sr(rnorm(500)),as.sr(runif(200)-0.5), as.sr(rnorm(30)),as.sr(rnorm(100))) etc <- sr_unpaired_test(inp) inp <- list(as.sr(rnorm(500)),as.sr(rnorm(100,mean=0.2,sd=1))) etc <- sr_unpaired_test(inp,contrasts=c(1,1),null.value=0.2) etc$conf.int
# basic usage set.seed(as.integer(charToRaw("set the seed"))) # default contrast is 1,-1,1,-1,1,-1 etc <- sr_unpaired_test(as.sr(matrix(rnorm(1000*6,mean=0.02,sd=0.1),ncol=6))) print(etc) etc <- sr_unpaired_test(as.sr(matrix(rnorm(1000*4,mean=0.0005,sd=0.01),ncol=4)), alternative='greater') print(etc) etc <- sr_unpaired_test(as.sr(matrix(rnorm(1000*4,mean=0.0005,sd=0.01),ncol=4)), contrasts=c(1,1,1,1),null.value=-0.1,alternative='greater') print(etc) inp <- list(as.sr(rnorm(500)),as.sr(runif(200)-0.5), as.sr(rnorm(30)),as.sr(rnorm(100))) etc <- sr_unpaired_test(inp) inp <- list(as.sr(rnorm(500)),as.sr(rnorm(100,mean=0.2,sd=1))) etc <- sr_unpaired_test(inp,contrasts=c(1,1),null.value=0.2) etc$conf.int
Computes the variance of the sample Sharpe ratio.
sr_variance(snr, n, cumulants)
sr_variance(snr, n, cumulants)
snr |
the population Signal Noise ratio. Often one will use the population estimate instead. |
n |
the sample size that the Shapre ratio is observed on. |
cumulants |
a vector of the third through fourth, or the third through seventh population cumulants of the random variable. More terms are needed for the higher accuracy approximation. |
The sample Sharpe ratio has variance of the form
where is the population Signal Noise ratio,
is the sample size,
is the population skewness,
and
is the population excess kurtosis, and
through
are the fifth through
seventh cumulants of the error term.
This form of the variance appears as Equation (4) in Bao.
See ‘The Sharpe Ratio: Statistics and Applications’, section 3.2.3.
the variance of the sample statistic.
Steven E. Pav [email protected]
Bao, Yong. "Estimation Risk-Adjusted Sharpe Ratio and Fund Performance Ranking Under a General Return Distribution." Journal of Financial Econometrics 7, no. 2 (2009): 152-173. doi:10.1093/jjfinec/nbn022
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
# variance under normality: sr_variance(1, 100, rep(0,5))
# variance under normality: sr_variance(1, 100, rep(0,5))
Computes the variance covariance matrix of sample Sharpe ratios.
sr_vcov(X,vcov.func=vcov,ope=1)
sr_vcov(X,vcov.func=vcov,ope=1)
X |
an |
vcov.func |
a function which takes an object of class |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
Given contemporaneous observations of
returns
streams, this function estimates the asymptotic variance
covariance matrix of the vector of sample Sharpes,
One may use the default method for computing covariance,
via the vcov
function, or via a 'fancy' estimator,
like sandwich:vcovHAC
, sandwich:vcovHC
, etc.
This code first estimates the covariance of the vector of
the vector
stacked on its Hadamard square,
. This is
then translated back to a variance covariance on the vector of
sample Sharpe ratios via the Delta method.
a list containing the following components:
SR |
a vector of (annualized) Sharpe ratios. |
Ohat |
a |
p |
the number of assets. |
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Lo, Andrew W. "The statistics of Sharpe ratios." Financial Analysts Journal 58, no. 4 (2002): 36-52. https://www.ssrn.com/paper=377260
sr-distribution functions, dsr
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr
,
summary.sr
X <- matrix(rnorm(1000*3),ncol=3) colnames(X) <- c("ABC","XYZ","WORM") Sigmas <- sr_vcov(X) # make it fat tailed: X <- matrix(rt(1000*3,df=5),ncol=3) Sigmas <- sr_vcov(X) if (require(sandwich)) { Sigmas <- sr_vcov(X,vcov.func=vcovHC) } # add some autocorrelation to X Xf <- filter(X,c(0.2),"recursive") colnames(Xf) <- colnames(X) Sigmas <- sr_vcov(Xf) if (require(sandwich)) { Sigmas <- sr_vcov(Xf,vcov.func=vcovHAC) } # should run for a vector as well X <- rnorm(1000) SS <- sr_vcov(X)
X <- matrix(rnorm(1000*3),ncol=3) colnames(X) <- c("ABC","XYZ","WORM") Sigmas <- sr_vcov(X) # make it fat tailed: X <- matrix(rt(1000*3,df=5),ncol=3) Sigmas <- sr_vcov(X) if (require(sandwich)) { Sigmas <- sr_vcov(X,vcov.func=vcovHC) } # add some autocorrelation to X Xf <- filter(X,c(0.2),"recursive") colnames(Xf) <- colnames(X) Sigmas <- sr_vcov(Xf) if (require(sandwich)) { Sigmas <- sr_vcov(Xf,vcov.func=vcovHAC) } # should run for a vector as well X <- rnorm(1000) SS <- sr_vcov(X)
Computes the Sharpe Ratio Information Coefficient of Paulsen and Soehl, an asymptotically unbiased estimate of the out-of-sample Sharpe of the in-sample Markowitz portfolio.
sric(z.s)
sric(z.s)
z.s |
an object of type |
Let be an observed
matrix whose
rows are i.i.d. normal. Let
and
be
the sample mean and sample covariance. The Markowitz portfolio is
which has an in-sample Sharpe of
The Sharpe Ratio Information Criterion is defined as
The expected value (over draws of and of future returns)
of the
is equal to the expected value of the out-of-sample
Sharpe of the (in-sample) portfolio
(again, over the same draws.)
The Sharpe Ratio Information Coefficient.
Steven E. Pav [email protected]
Paulsen, D., and Soehl, J. "Noise Fit, Estimation Error, and Sharpe Information Criterion." arxiv preprint (2016): https://arxiv.org/abs/1602.06186
Other sropt Hotelling:
inference()
# generate some sropts nfac <- 3 nyr <- 5 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("fix seed"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) srv <- sric(asro)
# generate some sropts nfac <- 3 nyr <- 5 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("fix seed"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) srv <- sric(asro)
Spawns an object of class sropt
.
sropt(z.s, df1, df2, drag = 0, ope = 1, epoch = "yr", T2 = NULL)
sropt(z.s, df1, df2, drag = 0, ope = 1, epoch = "yr", T2 = NULL)
z.s |
an optimum Sharpe ratio statistic. |
df1 |
the number of assets in the portfolio. |
df2 |
the number of observations. |
drag |
the 'drag' term, |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
epoch |
the string representation of the 'epoch', defaulting to 'yr'. |
T2 |
the Hotelling |
The sropt
class contains information about a rescaled T^2-statistic.
The following are list attributes of the object:
The (optimal) Sharpe ratio statistic.
The number of assets.
The number of observations.
The drag term, which is the 'risk free rate' divided by the maximum risk.
The 'observations per epoch'.
The string name of the 'epoch'.
For the most part, this constructor should not be called directly,
rather as.sropt
should be called instead to compute the
needed statistics.
a list cast to class sropt
, with the following attributes:
the optimal Sharpe statistic.
the number of assets.
the number of observed vectors.
the input drag
term.
the input ope
term.
the input epoch
term.
the Hotelling statistic.
2FIX: allow rownames?
Steven E. Pav [email protected]
Other sropt:
as.sropt()
,
confint.sr()
,
dsropt()
,
is.sropt()
,
pco_sropt()
,
power.sropt_test()
,
reannualize()
,
sropt_test()
# roll your own. ope <- 253 zeta.s <- 1.0 df1 <- 10 df2 <- 6 * ope set.seed(as.integer(charToRaw("fix seed"))) rvs <- rsropt(1,df1,df2,zeta.s,ope,drag=0) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope) print(roll.own) # put a bunch in. naming becomes a problem. rvs <- rsropt(5,df1,df2,zeta.s,ope,drag=0) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope) print(roll.own)
# roll your own. ope <- 253 zeta.s <- 1.0 df1 <- 10 df2 <- 6 * ope set.seed(as.integer(charToRaw("fix seed"))) rvs <- rsropt(1,df1,df2,zeta.s,ope,drag=0) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope) print(roll.own) # put a bunch in. naming becomes a problem. rvs <- rsropt(5,df1,df2,zeta.s,ope,drag=0) roll.own <- sropt(z.s=rvs,df1,df2,drag=0,ope=ope) print(roll.own)
Performs one sample tests of Sharpe ratio of the Markowitz portfolio.
sropt_test(X,alternative=c("greater","two.sided","less"), zeta.s=0,ope=1,conf.level=0.95)
sropt_test(X,alternative=c("greater","two.sided","less"), zeta.s=0,ope=1,conf.level=0.95)
X |
a (non-empty) numeric matrix of data values, each row independent,
each column representing an asset, or an object of
class |
alternative |
a character string specifying the alternative hypothesis,
must be one of |
zeta.s |
a number indicating the null hypothesis value. |
ope |
the number of observations per 'epoch'. For convenience of
interpretation, The Sharpe ratio is typically quoted in 'annualized'
units for some epoch, that is, 'per square root epoch', though returns
are observed at a frequency of |
conf.level |
confidence level of the interval. (not used yet) |
Suppose are
independent draws of a
-variate
normal random variable with mean
and covariance matrix
. This code tests the hypothesis
The default alternative hypothesis is the one-sided
but this can be set otherwise.
Note there is no 'drag' term here since this represents a linear offset of the population parameter.
See ‘The Sharpe Ratio: Statistics and Applications’, section 6.3.2.
A list with class "htest"
containing the following components:
statistic |
the value of the |
parameter |
a list of the degrees of freedom for the statistic. |
p.value |
the p-value for the test. |
conf.int |
a confidence interval appropriate to the specified alternative hypothesis. NYI. |
estimate |
the estimated optimal Sharpe, annualized |
null.value |
the specified hypothesized value of the optimal Sharpe. |
alternative |
a character string describing the alternative hypothesis. |
method |
a character string indicating what type of test was performed. |
data.name |
a character string giving the name(s) of the data. |
Steven E. Pav [email protected]
Pav, S. E. "The Sharpe Ratio: Statistics and Applications." CRC Press, 2021.
Other sropt:
as.sropt()
,
confint.sr()
,
dsropt()
,
is.sropt()
,
pco_sropt()
,
power.sropt_test()
,
reannualize()
,
sropt
# test for uniformity pvs <- replicate(128,{ x <- sropt_test(matrix(rnorm(1000*4),ncol=4),alternative="two.sided") x$p.value }) plot(ecdf(pvs)) abline(0,1,col='red') # input a sropt objects: nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) stest <- sropt_test(asro,alternative="two.sided")
# test for uniformity pvs <- replicate(128,{ x <- sropt_test(matrix(rnorm(1000*4),ncol=4),alternative="two.sided") x$p.value }) plot(ecdf(pvs)) abline(0,1,col='red') # input a sropt objects: nfac <- 5 nyr <- 10 ope <- 253 # simulations with no covariance structure. # under the null: set.seed(as.integer(charToRaw("be determinstic"))) Returns <- matrix(rnorm(ope*nyr*nfac,mean=0,sd=0.0125),ncol=nfac) asro <- as.sropt(Returns,drag=0,ope=ope) stest <- sropt_test(asro,alternative="two.sided")
Nineteen years of daily log returns on three stocks and an ETF.
data(stock_returns)
data(stock_returns)
An xts
object with 4777 observations and 4 columns.
The columns are the daily log returns for the tickers IBM, AAPL, SPY and XOM,
as sourced from Yahoo finance using the quantmod
package.
Daily returns span from January, 2000 through December, 2018.
Returns are ‘log returns’, which are the differences of the logs of
daily adjusted closing price series, as defined by Yahoo finance (thus presumably
including adjustments for splits and dividends). Dates of observations are
the date of the second close defining the return, not the first.
The author makes no guarantees regarding correctness of this data.
Steven E. Pav [email protected]
Data were collected on October 2, 2019, from Yahoo finance using the
quantmod
package.
if (require(xts)) { data(stock_returns) as.sr(stock_returns) }
if (require(xts)) { data(stock_returns) as.sr(stock_returns) }
Computes a ‘summary’ of an object, adding in some statistics.
## S3 method for class 'sr' summary(object, ...) ## S3 method for class 'sropt' summary(object, ...)
## S3 method for class 'sr' summary(object, ...) ## S3 method for class 'sropt' summary(object, ...)
object |
an object of class |
... |
additional arguments affecting the summary produced, though ignored here. |
Enhances an object of class sr
, sropt
or del_sropt
to also
include t- or T-statistics, p-values, and so on.
When an sr
object is input, the object cast to class summary.sr
with some
additional fields:
the equivalent t-statistic.
the p-value under the null.
the standard error of the Sharpe ratio.
When an sropt
object is input, the object cast to class summary.sropt
with some
additional fields:
the p-value under the null.
the SRIC value, see sric
.
Steven E. Pav [email protected]
Sharpe, William F. "Mutual fund performance." Journal of business (1966): 119-138. https://ideas.repec.org/a/ucp/jnlbus/v39y1965p119.html
Other sr:
as.sr()
,
confint.sr()
,
dsr()
,
is.sr()
,
plambdap()
,
power.sr_test()
,
predint()
,
print.sr()
,
reannualize()
,
se()
,
sr_equality_test()
,
sr_test()
,
sr_unpaired_test()
,
sr_vcov()
,
sr
# Sharpe's 'model': just given a bunch of returns. set.seed(1234) asr <- as.sr(rnorm(253*3),ope=253) summary(asr)
# Sharpe's 'model': just given a bunch of returns. set.seed(1234) asr <- as.sr(rnorm(253*3),ope=253) summary(asr)