# Load packages
 
# Core
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.2
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(tidyquant)
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo 
## ── Attaching core tidyquant packages ─────────────────────── tidyquant 1.0.11 ──
## ✔ PerformanceAnalytics 2.0.8      ✔ TTR                  0.24.4
## ✔ quantmod             0.4.28     ✔ xts                  0.14.1── Conflicts ────────────────────────────────────────── tidyquant_conflicts() ──
## ✖ zoo::as.Date()                 masks base::as.Date()
## ✖ zoo::as.Date.numeric()         masks base::as.Date.numeric()
## ✖ dplyr::filter()                masks stats::filter()
## ✖ xts::first()                   masks dplyr::first()
## ✖ dplyr::lag()                   masks stats::lag()
## ✖ xts::last()                    masks dplyr::last()
## ✖ PerformanceAnalytics::legend() masks graphics::legend()
## ✖ quantmod::summary()            masks base::summary()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggrepel)

Goal

Examine how each asset contributes to portfolio standard deviation. This is to ensure that our risk is not concentrated in any one asset.

1 Import stock prices

I chose the stocks NVDA, AMD, INTC, QCOM, AVGO

symbols <- c("NVDA", "AMD", "INTC", "QCOM", "AVGO")
 
prices <- tq_get(x    = symbols,
                 get  = "stock.prices",   
                 from = "2012-12-31",
                 to   = "2025-11-23")

2 Convert prices to returns (monthly)

asset_returns_tbl <- prices %>%
   
    group_by(symbol) %>%
   
    tq_transmute(select     = adjusted,
                 mutate_fun = periodReturn,
                 period     = "monthly",
                 type       = "log") %>%
   
    slice(-1) %>%
   
    ungroup() %>%
   
    set_names(c("asset", "date", "returns"))

3 Calculate Component Contribution to Portfolio Volatility

asset_returns_wide_tbl <- asset_returns_tbl %>%
 
    pivot_wider(names_from = asset, values_from = returns) %>%
 
    column_to_rownames(var = "date")
 
asset_returns_wide_tbl
##                     AMD          AVGO         INTC         NVDA          QCOM
## 2013-01-31  0.080042631  0.1223715009  0.020164044  0.000000000  0.0650839358
## 2013-02-28 -0.043228694 -0.0442993202  0.003056545  0.038221563 -0.0057722553
## 2013-03-28  0.023810626  0.0537860816  0.044951225  0.013338741  0.0232962903
## 2013-04-30  0.100643521 -0.1168089872  0.092225413  0.070706445 -0.0831348211
## 2013-05-31  0.349557500  0.1659702776  0.023060206  0.054651980  0.0300627242
## 2013-06-28  0.019802609 -0.0034658619 -0.002061275 -0.030167210 -0.0328476078
## 2013-07-31 -0.079021973 -0.0189043749 -0.037422708  0.028091529  0.0552469583
## 2013-08-30 -0.142285017  0.0486862799 -0.050298554  0.026270515  0.0315531082
## 2013-09-30  0.152839195  0.1180137070  0.041877208  0.053460262  0.0155694397
## 2013-10-31 -0.131658393  0.0531135335  0.065437486 -0.024066063  0.0317253729
## 2013-11-29  0.086012929 -0.0155281658 -0.016764777  0.032034585  0.0619539969
## 2013-12-31  0.061270767  0.1720590567  0.085191680  0.026567294  0.0090645214
## 2014-01-31 -0.120694197  0.0327415478 -0.056252361 -0.020177178 -0.0004038518
## 2014-02-28  0.078471606  0.1215174448  0.018415691  0.162107317  0.0143131483
## 2014-03-31  0.077759411  0.0472522625  0.041532646 -0.025903988  0.0509868943
## 2014-04-30  0.019753709 -0.0142289505  0.033526739  0.030788748 -0.0019042970
## 2014-05-30 -0.022250646  0.1069811385  0.031886464  0.032886222  0.0218655352
## 2014-06-30  0.046406386  0.0237166879  0.123136860 -0.024508306 -0.0104252407
## 2014-07-31 -0.069163352 -0.0380393494  0.092363787 -0.057729706 -0.0717021467
## 2014-08-29  0.064378658  0.1682173165  0.036569636  0.110060003  0.0372909934
## 2014-09-30 -0.201203738  0.0617383865 -0.002867734 -0.052782399 -0.0176313647
## 2014-10-31 -0.197092916 -0.0086579016 -0.023537313  0.057398853  0.0488093396
## 2014-11-28 -0.003577818  0.0796412483  0.097532549  0.074852327 -0.0683086431
## 2014-12-31 -0.043963081  0.0777639278 -0.026109310 -0.044863611  0.0194256384
## 2015-01-30 -0.038172628  0.0225105184 -0.093823440 -0.043318837 -0.1739881884
## 2015-02-27  0.190716820  0.2154937075  0.013482797  0.142698634  0.1491981489
## 2015-03-31 -0.148805873 -0.0020798842 -0.061395451 -0.052582437 -0.0388932997
## 2015-04-30 -0.170452010 -0.0828815813  0.040118355  0.058909294 -0.0195144526
## 2015-05-29  0.008810621  0.2365372043  0.064226833  0.001459480  0.0244057966
## 2015-06-30  0.051293347 -0.1050411858 -0.124698690 -0.095717045 -0.0997563934
## 2015-07-31 -0.217948801 -0.0603895495 -0.049530329 -0.007987836  0.0277138483
## 2015-08-31 -0.064193162  0.0066105042 -0.005990421  0.123595325 -0.1209126873
## 2015-09-30 -0.051002506 -0.0045194981  0.054546321  0.092151038 -0.0516838826
## 2015-10-30  0.209091727 -0.0151530047  0.116381107  0.140555322  0.1006589794
## 2015-11-30  0.107245540  0.0577493271  0.033540493  0.115405179 -0.1872810023
## 2015-12-31  0.195650415  0.1098535687 -0.009245897  0.038347304  0.0242975922
## 2016-01-29 -0.265854608 -0.0820948337 -0.104876885 -0.118048695 -0.0976335940
## 2016-02-29 -0.027651504  0.0019429754 -0.038432670  0.071923794  0.1228523304
## 2016-03-31  0.286513083  0.1458838206  0.089177565  0.127654968  0.0068673104
## 2016-04-29  0.219628629 -0.0583010614 -0.066126403 -0.002810703 -0.0121980073
## 2016-05-31  0.252565652  0.0573944161  0.050953761  0.276388593  0.0930625899
## 2016-06-30  0.117539811  0.0098647258  0.037587907  0.006187842 -0.0248885116
## 2016-07-29  0.288654408  0.0414703870  0.060911437  0.194443549  0.1554566217
## 2016-08-31  0.075772552  0.0853948901  0.036670380  0.073469226  0.0162518241
## 2016-09-30 -0.068510397 -0.0193431425  0.050526479  0.110693595  0.0825885830
## 2016-10-31  0.045269423 -0.0130690704 -0.079358468  0.037805333  0.0032065304
## 2016-11-30  0.208935186  0.0012327939  0.002655559  0.260525108 -0.0008315142
## 2016-12-30  0.241162087  0.0418729700  0.044251168  0.146435648 -0.0439580218
## 2017-01-31 -0.089419301  0.1209648631  0.015050662  0.022602381 -0.1990870571
## 2017-02-28  0.332469208  0.0557121175 -0.009868855 -0.071874969  0.0648199657
## 2017-03-31  0.006204787  0.0418879117 -0.003597297  0.070843454  0.0151116803
## 2017-04-28 -0.089826957  0.0084134055  0.002215375 -0.043433834 -0.0648405204
## 2017-05-31 -0.172743565  0.0811673820  0.006304483  0.326022248  0.0734189150
## 2017-06-30  0.109106841 -0.0229673720 -0.067885801  0.001453548 -0.0364517674
## 2017-07-31  0.086677465  0.0567574351  0.050003371  0.117044836 -0.0374550381
## 2017-08-31 -0.045855434  0.0216964976 -0.003862534  0.042639695 -0.0064316618
## 2017-09-29 -0.019418086 -0.0344611333  0.082343361  0.053601031 -0.0082609306
## 2017-10-31 -0.148545524  0.0844416098  0.177803081  0.145700288 -0.0161402804
## 2017-11-30 -0.009140779  0.0517972893 -0.008483167 -0.029245013  0.2711718434
## 2017-12-29 -0.057644734 -0.0721110396  0.029012645 -0.036583721 -0.0355976888
## 2018-01-31  0.290111036 -0.0351372161  0.042000556  0.239241270  0.0639819607
## 2018-02-28 -0.126279741 -0.0063499517  0.030369007 -0.014959636 -0.0402439264
## 2018-03-29 -0.186458876 -0.0303056003  0.055059722 -0.043969141 -0.1596270468
## 2018-04-30  0.079353598 -0.0267929331 -0.008872201 -0.029312612 -0.0827386519
## 2018-05-31  0.232656934  0.0941522165  0.072809007  0.115145044  0.1408822241
## 2018-06-29  0.087800110 -0.0248072857 -0.104757115 -0.062544835 -0.0350174797
## 2018-07-31  0.201155761 -0.0899382481 -0.032923919  0.033048806  0.1327960799
## 2018-08-31  0.317113754 -0.0124317410  0.012900459  0.137075382  0.0696065516
## 2018-09-28  0.204779675  0.1265657460 -0.023820489  0.001210692  0.0562519228
## 2018-10-31 -0.528461644 -0.0989278250 -0.008707685 -0.287373596 -0.1356952099
## 2018-11-30  0.156736193  0.0604220707  0.056809261 -0.253667834 -0.0764713586
## 2018-12-31 -0.143100857  0.0791298119 -0.049470056 -0.202282829 -0.0126469057
## 2019-01-31  0.279386699  0.0534835446  0.004040761  0.073974188 -0.1390944994
## 2019-02-28 -0.036716646  0.0261598639  0.123157658  0.071593884  0.0752467458
## 2019-03-29  0.081186244  0.0969600981  0.013876406  0.151869729  0.0775147762
## 2019-04-30  0.079439632  0.0571619355 -0.050803381  0.007987991  0.4122805193
## 2019-05-31 -0.007994204 -0.2353085969 -0.141405977 -0.288680055 -0.2538552678
## 2019-06-28  0.102547402  0.1440163226  0.083390516  0.192591354  0.1387503540
## 2019-07-31  0.002630713  0.0073721516  0.054474322  0.026972988 -0.0390052067
## 2019-08-30  0.032312988 -0.0256724777 -0.057400737 -0.006208336  0.0611071129
## 2019-09-30 -0.081448072 -0.0143600786  0.083330803  0.038414579 -0.0114368412
## 2019-10-31  0.157348653  0.0590064133  0.092607302  0.143946827  0.0531007618
## 2019-11-29  0.143100874  0.0767457965  0.032021744  0.076031415  0.0379336150
## 2019-12-31  0.158192829  0.0094774769  0.030536787  0.082163062  0.0622258062
## 2020-01-31  0.024554311 -0.0349691926  0.065947292  0.004790717 -0.0336554731
## 2020-02-28 -0.032874943 -0.1127574417 -0.136132889  0.133627179 -0.0857438151
## 2020-03-31  0.000000000 -0.1227418744 -0.025539484 -0.024248320 -0.1382769360
## 2020-04-30  0.141443063  0.1359220127  0.102807393  0.103279264  0.1509146007
## 2020-05-29  0.026557731  0.0698468383  0.053644740  0.194461799  0.0277045711
## 2020-06-30 -0.022367226  0.0905060949 -0.050515713  0.068216642  0.1279080762
## 2020-07-31  0.386468079  0.0036056387 -0.225947203  0.111189735  0.1465885782
## 2020-08-31  0.159505216  0.0916441182  0.072053235  0.231105164  0.1202102197
## 2020-09-30 -0.102282260  0.0573513188  0.016158964  0.011895803 -0.0066525938
## 2020-10-30 -0.085249927 -0.0411569324 -0.156470648 -0.076501434  0.0471379477
## 2020-11-30  0.207589583  0.1385310409  0.095180299  0.066921690  0.1764813813
## 2020-12-31 -0.010305505  0.0949545015  0.029950145 -0.025900267  0.0388329822
## 2021-01-29 -0.068478958  0.0284815375  0.108146794 -0.005010491  0.0255342441
## 2021-02-26 -0.013282561  0.0420977710  0.096749057  0.054293128 -0.1375984143
## 2021-03-31 -0.073771271 -0.0055179939  0.051622367 -0.026723313 -0.0220346286
## 2021-04-30  0.038975739 -0.0162202942 -0.106576559  0.117297943  0.0457724370
## 2021-05-28 -0.019048206  0.0347466409 -0.001012068  0.079070969 -0.0311726328
## 2021-06-30  0.159523669  0.0173004343 -0.017305829  0.208332464  0.0655832256
## 2021-07-30  0.122680138  0.0177921640 -0.044063048 -0.025494376  0.0469462705
## 2021-08-31  0.041774537  0.0240391965  0.012786545  0.138204178 -0.0209796714
## 2021-09-30 -0.073246845 -0.0177059044 -0.014533630 -0.077484748 -0.1240176874
## 2021-10-29  0.155648945  0.0920186447 -0.083740751  0.210396107  0.0309923584
## 2021-11-30  0.275527434  0.0405635592  0.011003486  0.245338475  0.3054134615
## 2021-12-31 -0.095815465  0.1901931856  0.045688233 -0.105149837  0.0164854526
## 2022-01-31 -0.230729542 -0.1272884175 -0.053441766 -0.183267084 -0.0396562652
## 2022-02-28  0.076555710  0.0026592014 -0.015619780 -0.004133359 -0.0216830148
## 2022-03-31 -0.120482480  0.0761769891  0.038252640  0.112576096 -0.1140126152
## 2022-04-29 -0.245712033 -0.1273432870 -0.128356236 -0.386065860 -0.0898349567
## 2022-05-31  0.174849100  0.0453838963  0.026735525  0.006717184  0.0249560698
## 2022-06-30 -0.286700804 -0.1691784100 -0.171751720 -0.208219456 -0.1091346281
## 2022-07-29  0.211383814  0.0973458850 -0.029844955  0.180792162  0.1271506888
## 2022-08-31 -0.107161630 -0.0703370571 -0.118815610 -0.185089223 -0.0867066714
## 2022-09-30 -0.292287949 -0.1086290426 -0.214021590 -0.217576889 -0.1576344623
## 2022-10-31 -0.053488672  0.0571406683  0.098233757  0.106044087  0.0405882821
## 2022-11-30  0.256609830  0.1588013386  0.069498834  0.226461963  0.0787199409
## 2022-12-30 -0.181111434  0.0229019120 -0.129028356 -0.146693509 -0.1402284624
## 2023-01-31  0.148643655  0.0452469137  0.066947689  0.290330053  0.1919921120
## 2023-02-28  0.044631101  0.0157385952 -0.113264151  0.172531463 -0.0754429421
## 2023-03-31  0.220952299  0.0836757225  0.270385215  0.179536623  0.0383496984
## 2023-04-28 -0.092284450 -0.0237227003 -0.050536327 -0.001008545 -0.0882806164
## 2023-05-31  0.279677610  0.2543637437  0.016246810  0.310008421 -0.0225331147
## 2023-06-30 -0.037054001  0.0763356640  0.061671765  0.111729633  0.0484499546
## 2023-07-31  0.004292397  0.0353588044  0.067356828  0.099530604  0.1046294120
## 2023-08-31 -0.078906991  0.0266165270 -0.014178264  0.054674143 -0.1361961617
## 2023-09-29 -0.027814218 -0.0999543969  0.011599853 -0.126218959 -0.0307663011
## 2023-10-31 -0.042923335  0.0129073788  0.026372304 -0.064546004 -0.0188144018
## 2023-11-30  0.207055468  0.0955511258  0.205943948  0.137050180  0.1752834663
## 2023-12-29  0.196105829  0.1917352680  0.117037092  0.057262971  0.1139789213
## 2024-01-31  0.128899206  0.0555394007 -0.153951666  0.217059119  0.0264733762
## 2024-02-29  0.138134928  0.0972266814  0.002230210  0.251388578  0.0656793327
## 2024-03-28 -0.064576574  0.0232305869  0.025683568  0.132939640  0.0704072904
## 2024-04-30 -0.130678165 -0.0191500659 -0.371303188 -0.044746473 -0.0205884618
## 2024-05-31  0.052397554  0.0215163024  0.016447718  0.238127430  0.2113710734
## 2024-06-28 -0.028502961  0.1925359758  0.003882183  0.119508692 -0.0241562563
## 2024-07-31 -0.115750773  0.0007906828 -0.007454249 -0.054220207 -0.0959882314
## 2024-08-30  0.027847841  0.0132306216 -0.326382133  0.019883219 -0.0317218484
## 2024-09-30  0.099365224  0.0610354808  0.062437778  0.017277876 -0.0252518272
## 2024-10-31 -0.130054782 -0.0159525136 -0.086314048  0.089122780 -0.0437542883
## 2024-11-29 -0.049005465 -0.0463546288  0.111152208  0.040520596 -0.0263942762
## 2024-12-31 -0.127240370  0.3606331958 -0.181905849 -0.028993152 -0.0262445749
## 2025-01-31 -0.040894471 -0.0466637674 -0.031410837 -0.111926722  0.1184048458
## 2025-02-28 -0.149389826 -0.1039204207  0.199921772  0.039598542 -0.0955587357
## 2025-03-31  0.028432293 -0.1718766200 -0.043934735 -0.141937940 -0.0175335609
## 2025-04-30 -0.053888788  0.1393750503 -0.122085482  0.004970095 -0.0341013903
## 2025-05-30  0.128782120  0.2292866468 -0.027744587  0.215623691 -0.0222033127
## 2025-06-30  0.248027705  0.1322559258  0.136085694  0.156363898  0.0984148325
## 2025-07-31  0.217121254  0.0634269180 -0.123379043  0.118521141 -0.0817394491
## 2025-08-29 -0.080766083  0.0124855590  0.206849966 -0.020963722  0.0909274171
## 2025-09-30 -0.005178555  0.1054873450  0.320504975  0.068827263  0.0401029048
## 2025-10-31  0.459346909  0.1136826174  0.175592624  0.081830424  0.0837902561
## 2025-11-21 -0.228605091 -0.0829688027 -0.147670141 -0.123975327 -0.1023554008
covariance_matrix <- cov(asset_returns_wide_tbl)
 
covariance_matrix
##              AMD        AVGO        INTC        NVDA        QCOM
## AMD  0.024257654 0.005227882 0.003571723 0.010406738 0.006640727
## AVGO 0.005227882 0.007718952 0.002395779 0.004908708 0.002931620
## INTC 0.003571723 0.002395779 0.009137455 0.003786169 0.002205370
## NVDA 0.010406738 0.004908708 0.003786169 0.014694213 0.005222502
## QCOM 0.006640727 0.002931620 0.002205370 0.005222502 0.009235837
w <- c(0.25, 0.25, 0.2, 0.2, 0.1)
 
sd_portfolio <- sqrt(t(w) %*% covariance_matrix %*% w)
sd_portfolio
##            [,1]
## [1,] 0.08309374
component_contribution <- (t(w) %*% covariance_matrix * w) / sd_portfolio[1,1]
component_contribution
##             AMD       AVGO       INTC       NVDA       QCOM
## [1,] 0.03258714 0.01501548 0.01034287 0.01936894 0.00577931
rowSums(component_contribution)
## [1] 0.08309374
component_percentages <- (component_contribution / sd_portfolio[1,1]) %>%
    round(3) %>%
    as_tibble()
 
component_percentages
## # A tibble: 1 × 5
##     AMD  AVGO  INTC  NVDA  QCOM
##   <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0.392 0.181 0.124 0.233  0.07
component_percentages %>%
 
    as_tibble() %>%
    gather(key = "asset", value = "contribution")
## # A tibble: 5 × 2
##   asset contribution
##   <chr>        <dbl>
## 1 AMD          0.392
## 2 AVGO         0.181
## 3 INTC         0.124
## 4 NVDA         0.233
## 5 QCOM         0.07
# Transform data into wide form
asset_returns_wide_tbl <- asset_returns_tbl %>%
 
    pivot_wider(names_from = asset, values_from = returns) %>%
 
    column_to_rownames(var = "date")
 
# Custom function
calculate_component_contribution <- function(asset_returns_wide_tbl, w) {
 
    # Covariance of asset returns
    covariance_matrix <- cov(asset_returns_wide_tbl)
   
    # Standard deviation of portfolio
    sd_portfolio <- sqrt(t(w) %*% covariance_matrix %*% w)
 
    # Component contribution
    component_contribution <- (t(w) %*% covariance_matrix * w) / sd_portfolio[1,1]
 
    # Component contribution in percentage
    component_percentages <- (component_contribution / sd_portfolio[1,1]) %>%
        round(3) %>%
        as_tibble()
   
    return(component_percentages)
 
}
 
asset_returns_wide_tbl %>% calculate_component_contribution(w = c(0.25,0.25,0.2,0.2,0.1))
## # A tibble: 1 × 5
##     AMD  AVGO  INTC  NVDA  QCOM
##   <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0.392 0.181 0.124 0.233  0.07

6 Plot: Colum Chart of Component Contribution and Weight

asset_returns_wide_tbl %>%
 
    calculate_component_contribution(w = c(0.25,0.25,0.2,0.2,0.1)) %>%
    gather(key = "asset", value = "contribution") %>%
 
    ggplot(aes(asset, contribution)) +
    geom_col(fill = "cornflowerblue") +
   
    theme(plot.title = element_text(hjust = 0.5)) +
    scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
   
    labs(title = "Percent Contribution to Portfolio Standard Deviation",
         y = "Percent Contribution to Risk",
         x = NULL)

asset_returns_wide_tbl %>%
 
    calculate_component_contribution(w = c(0.25,0.25,0.2,0.2,0.1)) %>%
    gather(key = "asset", value = "contribution") %>%
    add_column(weights = c(0.25,0.25,0.2,0.2,0.1)) %>%
    pivot_longer(cols = c(contribution, weights), names_to = "type", values_to = "value") %>%
 
    ggplot(aes(asset, value, fill = type)) +
    geom_col(position = "dodge") +
   
    theme(plot.title = element_text(hjust = 0.5)) +
    scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
    theme_tq() +
    scale_fill_tq() +
 
    labs(title = "Percent Contribution to Volatility",
         y = "percent",
         x = "asset")

Which of the assets in your portfolio the largest contributor to the portfolio volatility? Do you think your portfolio risk is concentrated in any one asset?

Based on the bar chart, AMD is the largest contributor to the portfolio’s volatility, accounting for roughly 39–40% of the total risk. In terms of concentration, the portfolio is somewhat concentrated in AMD since its contribution is noticeably higher than any other holding. However, AMD isn’t the only meaningful source of risk, NVDA also represents a significant portion (around 23%), and AVGO contributes a moderate amount (around 18%). INTC and QCOM, by comparison, have much smaller impacts on overall volatility.