knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  echo = TRUE
)
knitr::opts_knit$set(
  root.dir = normalizePath('..')
)

library(xts)
library(plm)
library(FactorAnalytics)

Introduction

In these notes we replicate \textcite{asness-moskowitz-pedersen-2013} (AMP hereafter). The authors aim at providing a global, across both markets/asset classes and across countries, asset pricing approach. This is what "everywhere" in "Value and Momentum Everywhere" stands for.

Data and Portfolio Construction

Factors and portfolios are constructed from January 1972 through July 2011. All data series have a monthly frequency. AMP construct zero-cost long/short VME factors and long-only value and momentum portfolios, sorted into tertiles, for both value and momentum in eight markets/asset classes.

Because of the global nature of their pricing approach, authors use as their $MKT$ factor an international portfolio proxy, represented by the MSCI World Index. The index has 1,637 large and mid cap constituents across 23 Developed Markets countries and, according to MSCI Inc., it "covers approximately 85% of the free float-adjusted market capitalization in each of them." We provide a comparison with the international global equity portfolio constructed by \textcite{asness-frazzini-2013}.

# Source AQR's Global Aggregate Equity Portfolio ("AQR GAP")
# NOTE: returns in decimal unit
# source("vignettes/Value--Devil-in-HMLs-Details.R")
library(xts)
parser.path <- system.file('parsers',        
                           'Devil-in-HML-Details.R',
                           package='ExpectedReturns')
source(parser.path)

# Subset AQR GAP data points 
MKT.GLOBAL <- HML_Devil.MKT[,"Global"]
colnames(MKT.GLOBAL) <- 'MKT.GLOBAL'

# delete leading NAs
MKT.GLOBAL <- na.trim(MKT.GLOBAL)

#tclass(MKT.GLOBAL) <- "yearmon"

# Correlation methods to pass to cor()
cor.methods <- c('pearson', 'spearman')
# NOTE: returns in decimal unit
path.parser <- system.file('parsers', 
                           'MSCI-WI.R', 
                           package = 'ExpectedReturns')
source(path.parser)

#change date to end-of-month
# tclass(MSCI.WI) <- yearmon(index(MSCI.WI))
# Merge and compute correlation(s)
global.proxies <- merge(MKT.GLOBAL, MSCI.WI$COMP.RET)

PerformanceAnalytics::table.Correlation(global.proxies$MKT.GLOBAL, global.proxies$COMP.RET,
                                          method = "pearson")
PerformanceAnalytics::chart.Correlation(global.proxies)

# Plot comparison
# global.proxies.cont <- na.locf(global.proxies)
global.proxies.cont <- na.trim(global.proxies)

plot.global.proxies.cont <- function(x=global.proxies.cont) {
  plot.xts(x, 
           main='AQR\'s Global MKT vs MSCI World Index',
           screens=factor(1, 1), 
           col=c('black', 'blue'), 
           lwd=c(0.95, 0.95)
           )
  addLegend('bottomleft', 
            legend.names = c('Global MKT', 'MSCI WI', 
                             expression(paste(rho[Pearson], ' = 0.9620982   '))
                             ), 
            cex=0.75, lty=1, lwd=1, 
            col=c('black', 'blue', 
                         NA, NA)
            )
}

plot.global.proxies.cont()

Because AQR's "Global Aggregate Equity Portfolio" contains more securities then the MSCI WI, we also provide a comparison with the MSCI All Countries World Index (MSCI ACWI). The latter synthesizes 3,040 large and mid cap constituents across 23 Developed Markets and 26 Emerging Markets countries and, according to MSCI Inc., it "covers approximately 85% of the global investable equity opportunity set".

# NOTE: returns in decimal unit
path.parser <- system.file('parsers', 'MSCI-ACWI.R', package='ExpectedReturns')
source(path.parser)

#change date to end-of-month
#index(MSCI.ACWI) <- as.Date(timeDate::timeLastDayInMonth(index(MSCI.ACWI)))
# Merge and compute correlation(s)
global.proxies <- merge(MKT.GLOBAL, MSCI.ACWI$COMP.RET)

PerformanceAnalytics::table.Correlation(global.proxies$MKT.GLOBAL, global.proxies$COMP.RET)
PerformanceAnalytics::chart.Correlation(global.proxies)


# Plot comparison
# global.proxies.cont <- na.locf(global.proxies)
global.proxies.cont <- na.fill(global.proxies, c(NA, 'extend', 'extend'))
plot.global.proxies.cont <- function(x=global.proxies.cont) {
  plot.xts(
    x, 
    main='AQR\'s Global MKT vs MSCI ACWI',
    screens=factor(1, 1), col=c('black', 'blue'), lwd=c(0.95, 0.95)
  )
  addLegend(
    'bottomleft', 
    legend.names = c(
      'Global MKT', 'MSCI ACWI', 
      expression(paste(rho[Pearson], ' = 0.9642701'))
    ), 
    cex=0.75, lty=1, lwd=1, col=c('black', 'blue', NA, NA)
  )
}
plot.global.proxies.cont()

As shown by the high correlations among these global equity portfolios, in principle each one of them may serve as the market portfolio return proxy. However, in order to avoid any potentially undue discrepancy with AMP's methodology, in what follows we adopt the MSCI WI.

As per the bonds factors, AMP report to use \textcite{fama-french-1992}'s bond factors, namely the $TERM$ and $DEF$ factors. In their seminal work, \textcite{fama-french-1992} introduced these factors to capture common risks between equities and fixed-income securities. They defined $TERM$ to proxy unexpected changes in interest rates and constructed it via two composite portfolios of government bonds covering maturities of 1 to 5 and 6 to 10 years in excess of T-Bill monthly rates. Whereas $DEF$ is a proxy of default risk and is obtained as the excess returns on a portfolio of long-term corporate bonds made of Moody's Aaa, Aa, A, Baa, and below (LG, low-grade bonds below Baa) with respect to corresponding T-Bonds. Furthermore, both of them were originally considered limiting attention to the US markets, which in principle does not naturally fit many of the international \textcite{asness-moskowitz-pedersen-2013}'s analyses and hence may seem an undue questionable adoption at a first glance. Truth is, authors are aware of this limitation and unequivocally state "... since we do not have data to construct TERM and DEF internationally, we use the U.S. versions" (p. 956). In our experience we can confirm, lack of worldwide long series available is generally the case for corporate bonds for which there could be even unrecorded decades depending on the countries considered. Even widespread industry benchmarks such as the FTSE WGBI (formerly by Salomon Brothers, later by Citi, then LSEG), and the S&P International Corporate Bond Index, respectively, are the result of different methodologies in a number of relevant characteristics: underlying maturities, whether there has been currency hedging or inflation adjustment, and importantly securities' credit ratings and selection criteria. Also, sometimes underlings are not precisely the same securities, but broader forms of debt.

In the face of these limitations, we temporarily use bonds factors US versions as authors themselves do. However, it is debatable whether we are using exactly the same series. We are adopting part of those constructed by \textcite{asvanunt-richardson-2017}, who follow a different method in some regards. As a matter of fact, slight nuances may potentially influence our numerical estimates, but are not expected to give rise to statistically significant differences.

Asset classes and countries

The markets/asset classes contemplated include four individual equity markets (stock selection) and four broad asset classes (asset allocation). The four stock selection markets are: U.S. equities (US), U.K. equities (UK), Continental Europe equities (EU), Japanese equities (JP). The four asset allocation classes are: global equity indices (EQ), currencies (FX), fixed income (FI), and commodity futures (CM).

They are global individual stocks, global equity indices, currencies, global government bonds, commodity futures (27 types by different underlying),

While the countries are...

Value and momentum measures

Relation to Macroeconomic and Liquidity Risk

This section of \textcite{asness-moskowitz-pedersen-2013} blends very well with \textcite{ilmanen-2011}'s "Broader Themes", especially with what are commonly referred to as "macroeconomic factors" or simply "macros" in the financial industry jargon. Indeed, at a first sight it appears a more quantitative-oriented look at many ideas the former author already discussed. In particular, the authors study the underlying economic sources driving value and momentum returns: Long run consumption growth MSCI world index ("market") Recession dummy GDP growth * Liquidity

The Liquidity Risk Exposure is particularly interesting to study and deserves particular attention. In this context, it is so especially from the point of view of market-level traded liquidity, given the ubiquitous influence of the factor for the entire economic system. It really has been in Economists' thoughts since far longer than the modern standard literature reminds us. Recently it has been linked with financial crises (see what happened in 1987), flash crashes (2010), and vice versa is very often in policy makers' decisions. See, for example, monetary policies actions Central Banks adopt worldwide in this regard. \textcite{asness-moskowitz-pedersen-2013} pay particular attention to global events and link them with liquidity shocks. Then, they investigate the relation between value and momentum returns and liquidity risk.

Comovement and Asset Pricing Tests

## Import AQR data

# VME data
source(system.file('parsers', 'VME-Factors-orig.R',package='ExpectedReturns'))
source(system.file('parsers', 'VME-Portfolios.R',package='ExpectedReturns'))

# Bonds data
source(system.file('parsers', 'CRP.R',package='ExpectedReturns'))
# function for computing annualized return, standard deviation, and sharpe (need to still incorporate t-stats, P3-P1 column, 50/50 comb column, &  corresponding alphas)

VME_table1 <- function(VME.Portfolios, ...) {

return <- PerformanceAnalytics::Return.annualized(VME.Portfolios, ...)
SD <- PerformanceAnalytics::sd.annualized(VME.Portfolios, ...)
sharpe <- PerformanceAnalytics::SharpeRatio.annualized(VME.Portfolios, ...)

  return(rbind(return, SD, sharpe) )
}

VME_table1(VME.Portfolios, geometric = FALSE)
## Construct the data set
# NOTE:
# All series are considered relative to FF dates, which are month-end.
# Usually a dates mismatch of one day can exist around the month-end, for reasons 
# among which publication date discrepancies or later corrections. 
# When this happens we simply consider last available values with respect to the 
# month-end, as those are dates most series we work with refer to.

# Value and Momentum Everywhere Factors
#VME.FACTORS <- VME.Factors.orig[, c('VAL.EVR', 'MOM.EVR')]
VME.FACTORS <- VME.Factors[, c('VAL.EVR', 'MOM.EVR')]

# Get AMP Portfolios Returns and make them a panel
VME.PORTF.DATE <- index(VME.Portfolios)
#indexVME.Portfolios$DATE <- NULL
# VME.PORTF.RET <- coredata(VME.Portfolios)
VME.PORTF.RET <- VME.Portfolios
# portf.names <- colnames(VME.PORTF.RET)
# t <- nrow(VME.PORTF.RET)
# p <- ncol(VME.PORTF.RET)
# portf.names <- as.data.frame(matrix(portf.names, nrow=t, ncol=p, byrow=TRUE))
# VME.PORTF.RET <- asplit(VME.PORTF.RET, 2)
# portf.names <- asplit(portf.names, 2)
# VME.PORTF.RET <- Map(cbind, portf.names, VME.PORTF.RET)
# VME.PORTF.RET <- Reduce(rbind, VME.PORTF.RET)
# VME.PORTF.RET <- data.frame(PORTF=VME.PORTF.RET[, 1], RET=VME.PORTF.RET[, 2])
# VME.PORTF.RET <- cbind(DATE=rep(VME.PORTF.DATE, p), VME.PORTF.RET)
# # VME.PORTF.RET$DATE <- as.Date(VME.PORTF.RET$DATE)
# VME.PORTF.RET$RET <- as.numeric(VME.PORTF.RET$RET)
source(system.file('parsers', 'FFdownloads_factors_package.R',package='ExpectedReturns'))
## Function to parse through 'ffdownloads' package available monthly data from "http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html" - Bryan
FFfactors <- FFfactors_xts(x)
## assigning same name as used in this and other scripts.
FF3 <- FFfactors[["x_F-F_Research_Data_Factors"]]
FF5 <- FFfactors[["x_F-F_Research_Data_5_Factors_2x3"]]
MOM <- FFfactors[["x_F-F_Momentum_Factor"]]
## Get Fama-French-Carhart Four Factors (MKT.RF, SMB, HML, MOM)
#FF3 <- ExpectedReturns::GetFactors('FF3', freq='monthly')
#MOM <- ExpectedReturns::GetFactors('MOM', freq='monthly')

## aded as.Date() wrap since date format in new data comes in as "%b %Y" (May 2022). Need to properly adjust FFfactors function so this is taken care of before.
# min.tp <- max(first(as.Date(index(FF3))), first(as.Date(index(MOM))))
# max.tp <- min(last(as.Date(index(FF3))), last(as.Date(index(FF3))))
# tp <- paste(min.tp, max.tp, sep='/')
# days.diff <- diff(seq.Date(min.tp, max.tp, by='month'))[-1]
# ff.dates <- c(min.tp, min.tp + cumsum(as.numeric(days.diff)))
## Merge Factors and Portfolios Data
# Factors data
FFC4 <- na.trim( merge(FF3, MOM) )
portfolios <- merge(MSCI.WI, FFC4)
# MSCI  
portfolios$MSCI.RF <- zoo::na.locf(portfolios$RET)

# portfolios$MSCI.RF <- portfolios$RET
# - portfolios$RF
# Bonds factors data
CRP <- CRP[, colnames(CRP) != 'SP500.XS']

#change date to end-of-month
#index(CRP) <- as.Date(timeDate::timeLastDayInMonth(index(CRP)))
portfolios <- merge(CRP, portfolios)
# Merge factors data
# portfolios <- zoo::na.locf(portfolios)
# portfolios <- portfolios.frame(
#   DATE=ff.dates,
#   portfolios[ff.dates, c(colnames(CRP), 'MSCI.RF', colnames(FFC4))],
#   row.names=NULL
# )
#portfolios <- portfolios[,c(colnames(CRP), 'MSCI.RF', colnames(FFC4))]
#tp <- 1:max(which(!is.na(portfolios$CORP.XS)))
#portfolios <- portfolios[tp, ]

# Merge VME Factors and VME Portfolios Returns
# TODO: use xts, but this is a minimal fix to ensure document knits successfully as we use 'DATE' column lower down
# data1 <- merge(VME.FACTORS, VME.PORTF.RET, by='DATE')


# VME.FACTORS_df <- as.data.frame(VME.FACTORS) # See above TODO
# data_df <- as.data.frame(data)
# data_df$DATE <- index(data)

portfolios <- merge(VME.FACTORS, portfolios)

## adding columns of excess returns from time series regression done in {r Global Three-Factor Model 2} chunk, (line ~348). Adding columns to 'portfolios' object will hopefully allow to use FactorAnalytics::FitTsfm and keep data as xts object. Will replicated data object 'portfolios2' to keep other code workable in case we decide to use plm to run regression instead. 
# 
portfolios2 <- portfolios

portfolios2$EXC.RET.VAL.EVR <- portfolios2$VAL.EVR - portfolios2$RF
portfolios2$EXC.RET.MOM.EVR <- portfolios2$MOM.EVR - portfolios2$RF
portfolios2$EXC.RET.MSCI.RF <- portfolios2$MSCI.RF - portfolios2$RF



# VME.FACTORS_df$DATE <- index(VME.FACTORS) # See above TODO...
## may not need these lines of code below
# data <- merge(data, VME.FACTORS_df, by='DATE')
## old merge code...
## portfolios <- merge(portfolios, VME.PORTF.RET) #, by='DATE')
## portfolios <- merge(portfolios, VME.FACTORS) #, by='DATE')

# Calculate Portfolios excess returns
# portfolios$EXC.RET <- portfolios$RET - portfolios$RF
portfoliosDT <- data.table::data.table(Date = index(portfolios), coredata(portfolios))
portfoliosDT <- data.table::melt(data = portfoliosDT, id.vars = c("Date"), value.name = "RET", variable.name = "PORTFNAME")
data.table::setkey(portfoliosDT,"Date","PORTFNAME")

tmp <- portfoliosDT[PORTFNAME == "RF"]
tmp[,EXC.RET:=NA]
nrow(portfoliosDT)

portfoliosDT <- 
data.table::merge.data.table(x = portfoliosDT[PORTFNAME != "RF"]
                  , y = portfoliosDT[PORTFNAME == "RF"]
                  , by = "Date"
)[,.(Date, PORTFNAME=PORTFNAME.x, RET=RET.x, EXC.RET=RET.x - RET.y)]

portfoliosDT <- rbind(portfoliosDT, tmp)
rm(tmp)

portfoliosDT <- portfoliosDT[!is.na(RET),]

Explaining Value/Momentum in One Market with Value/Momentum in Other Markets

$$ R_{i,t}^{p} - r_{f,t} = \alpha_{i}^{p} + \beta_{i}^{p}MKT_{t} + v_{i}^{p}\sum_{j \neq i}w_{j}VAL_{t}^{\textrm{everywhere}} + m_{i}^{p}\sum_{j \neq i}w_{j}MOM_{t}^{\textrm{everywhere}} + \epsilon_{i,t}^{p} $$ where $w_{j}$ is the equal volatility weight for each asset class.

Global Three-Factor Model

$$ R_{i,t}^{p} - r_{f,t} = \alpha_{i}^{p} + \beta_{i}^{p}MKT_{t} + v_{i}^{p}VAL_{t}^{\textrm{everywhere}} + m_{i}^{p}MOM_{t}^{\textrm{everywhere}} + \epsilon_{i,t}^{p} $$ $R_{i,t}^{p}$ is the time $t$ return to portfolio $p$ among the six high, middle, and low value and momentum portfolios in one of the eight asset markets $i$, for a total of 48 test assets. Whereas, $VAL_{t}^{\textrm{everywhere}}$ and $MOM_{t}^{\textrm{everywhere}}$ are equal-volatility-weighted across asset-class value and momentum factors.

## Prepare indexes to determine panel structure
# 'DATE.ID', each date gets unique numeric id
dates <- unique(portfoliosDT$Date)
dates.id <- 1:length(dates)
dates.id <- data.frame('Date'=dates, 'DATE.ID'=dates.id)
portfoliosDT <- merge(portfoliosDT, dates.id, by="Date")
# 'PORTF.ID', each portfolio gets unique numeric id
portf <- unique(portfoliosDT$PORTFNAME)
portf.id <- 1:length(portf)
portf.id <- data.frame('PORTFNAME'=portf, 'PORTF.ID'=portf.id)
portfoliosDT <- merge(portfoliosDT, portf.id, by='PORTFNAME')

# portfoliosDT <- portfoliosDT[order(portfoliosDT[, 'PORTF.ID'], portfoliosDT[, 'DATE.ID']), ]

portfoliosDT <- portfoliosDT[order(portfoliosDT[, 'PORTF.ID']), ]

portfoliosDT$PORTFNAME <- as.character(portfoliosDT$PORTFNAME)
# portfoliosDT <- portfoliosDT[order(portfoliosDT[, 'DATE.ID']), ]
row.names(portfoliosDT) <- NULL
# TODO: Only model for which data is readily available at the moment.
library(FactorAnalytics)

## attempting to reproduce regression below using fitTsfm instead

ts.reg2 <- fitTsfm(asset.names = colnames(portfolios2[,(11:13)]),
                   factor.names = colnames(portfolios2[,c("VAL.EVR", 
                                                          "MOM.EVR", 
                                                          "MSCI.RF")]),
                   data = portfolios2)

## Estimates
summary(ts.reg2) # average coefficients
coef(ts.reg2)

# Time-series regressions
ts.reg <- plm::pmg(EXC.RET ~ MSCI.RF + VAL.EVR + MOM.EVR,
                   data=portfoliosDT, 
                   index=c('PORTF.ID', 'DATE.ID')
                   )

## Estimates
summary(ts.reg) # average coefficients
betas <- t(ts.reg2$indcoef) # all coefficients
rownames(betas) <- portf
colnames(betas) <- paste('BETA', colnames(betas), sep='.')
betas

# Cross-sectional regressions
cs.portfolios <- portfolios[order(portfolios[, 'DATE']), ]
cs.portfolios <- data.frame(
  portfolios[, c('DATE', 'EXC.RET', 'DATE.ID', 'PORTF.ID')], 
  betas[, 2:ncol(betas)],
  row.names=NULL
)
cs.reg <- plm::pmg(
  EXC.RET ~ BETA.MSCI.RF + BETA.VAL.EVR + BETA.MOM.EVR, 
  data=cs.portfolios, index=c('DATE.ID', 'PORTF.ID')
)
## Estimates
summary(cs.reg) # average coefficients
gammas <- cs.reg$indcoef # all coefficients
gammas <- t(gammas)
gammas <- data.frame(dates, gammas, row.names=NULL)
colnames(gammas) <- c('DATE', '(Intercept)', colnames(betas)[-1])
gammas
## TABLE 6, Panel A
## US
us.factors.vars <- c('MKT.RF', 'SMB', 'HML', 'MOM', 'GOVT.XS', 'CORP.XS') 
n.us.fv <- length(us.factors.vars)
fv.seq <- 1:n.us.fv
# US Models Time-series Regressions
ts.reg.us <- lapply(fv.seq, function(x) {
  model.formula <- formula(
    paste0(
      'EXC.RET ~ ',
      paste(us.factors.vars[1:x], collapse='+')
    )
  )
  plm::pmg(
    model.formula, data=portfolios, 
    index=c('PORTF.ID', 'DATE.ID')
  )
})
us.mods.summaries <- lapply(ts.reg.us, summary)
us.mods.summaries

## Global
global.factors.vars <- c('MSCI.RF', 'VAL.EVR', 'MOM.EVR', 'GOVT.XS', 'CORP.XS')
nfv <- length(global.factors.vars)
kk <- 2^nfv - 1
fv.seq <- 1:nfv
global.factors.combns <- matrix(NA, nfv, kk)
comb.idxs <- cumsum(choose(nfv, 0:nfv))
comb.idxs <- comb.idxs[-length(comb.idxs)]
comb.idxs <- rev(comb.idxs)
comb.idxs <- c(comb.idxs, 0)
for (j in rev                             (fv.seq)) {
  k <- setdiff(1:comb.idxs[j], 0:comb.idxs[j+1])
  global.factors.combns[1:rev(j), k] <- combn(global.factors.vars, j)
}
# Global Models Time-series Regressions
ts.reg.global <- lapply(1:kk, function(x) {
  model.formula <- formula(
    paste0(
      'EXC.RET ~ ',
      paste(na.omit(global.factors.combns[, x]), collapse='+')
    )
  )
  plm::pmg(
    model.formula, data=portfolios, 
    index=c('PORTF.ID', 'DATE.ID')
  )
})
global.mods.summaries <- lapply(ts.reg.global, summary)
global.mods.summaries

## Models Stats
stats <- c('Abs.Alpha', 'SE', 'p.value', 'R2')
n.stats <- length(stats)
ExtractModelStats <- function(mods, res) {
  # @param mods Models fitting summaries
  # @param res A matrix, object to write results to
  mm <- length(mods)
  for(m in 1:mm) {
    mod <- mods[[m]]
    mod.coefs <- mod$CoefTable
    res[m, 1] <- abs(mod.coefs[1, 'Estimate'])
    res[m, 2] <- mod.coefs[1, 'Std. Error']
    res[m, 3] <- mod.coefs[1, 'Pr(>|z|)']
    res[m, 4] <- mod$r.squared
  }
  return(res)
}
# Calls on models
# US Models Results
us.mods.names <- c('CAPM', 'MKT.RF_SMB', 'FF3', 'FFC4', 'FF5', 'FF6')
n.us.mods <- length(us.mods.names)
us.mods.res <- matrix(NA, n.us.mods, n.stats, dimnames=list(us.mods.names, stats))
us.mods.res <- ExtractModelStats(us.mods.summaries, us.mods.res)
us.mods.res
# Global Models Results
gmods.names <- apply(global.factors.combns, 2, function(x) paste(na.omit(x), collapse='_'))
n.gmods <- length(gmods.names)
global.mods.res <- matrix(NA, n.gmods, n.stats, dimnames=list(gmods.names, stats))
global.mods.res <- ExtractModelStats(global.mods.summaries, global.mods.res)
global.mods.res
# All together
rbind(global.mods.res, us.mods.res)
### AMP Model Plot ###
VME.Portfolios <- apply(VME.Portfolios, 2, as.numeric)
amp.factors <- portfolios[1:length(dates), c('MKT.RF', 'VAL.EVR', 'MOM.EVR')]
amp.factors.avg <- colMeans(amp.factors, na.rm=TRUE)
# Actual Average Returns 
# TODO: Full series lengths differ. Should use only obs in regressions
y <- colMeans(VME.Portfolios, na.rm=TRUE)
y <- y[sort(names(y))]
# Expected Returns
x <- betas %*% c(1, amp.factors.avg)
x <- x[sort(row.names(x)), ]
# Plot
plot(x=x, y=y, main='AMP Global Three-factor Model', 
     xlab='Portfolio Expected Returns', ylab='Portfolio Realized Average Returns', 
     pch=20, cex.lab=0.85)
lines(par()$usr[1:2], par()$usr[3:4], col='gray75')
text(x=x, y=y, names(x), cex=0.5, pos=4)
grid()


JustinMShea/ExpectedReturns documentation built on Sept. 9, 2023, 9:41 p.m.