This vignette provides an overview of a practical use of opendatauzb package’s functions to work with Uzbek Stock Market Data. Unlike stock markets in developed economies, there are few resources available in Uzbekistan that are machine-friendly and able to provide Uzbek stock market prices in machine-readable formats. With this R package, we overcome these data limitations and improve productivity of equity research analysts while evaluating Uzbek stocks.
opendatauzb::RegisteredSecurities() collects a list of securities from the Central Securities Depository database.
| Issuer | SecurityCode | ISIN | CFI | last_update |
|---|---|---|---|---|
| " Andijon viloyati mashina-traktor parklari birlashmasi" aksiyadorlik jamiyati | UZ7046520001 | UZ0000000CY7 | ESXXXR | 11-09-2020 |
| “1-Temir beton mahsulotlari zavodi” aksiyadorlik jamiyati | UZ7042380004 | UZ0000000CQ3 | ESXXFR | 11-09-2020 |
| “4-sonli Montaj Boshqarmasi” aksiyadorlik jamiyati | UZ705326K015 | UZ0000000C74 | EPXXXR | 11-09-2020 |
| “4-sonli Montaj Boshqarmasi” aksiyadorlik jamiyati | UZ7053260004 | UZ0000000C82 | ESXXFR | 11-09-2020 |
| “Agregat zavodi” aksiyadorlik jamiyati | UZ7045360003 | UZ0000000FA0 | ESXXFR | 11-09-2020 |
| “Agro invest sug’urta” aksiyadorlik jamiyati | UZ7055650004 | UZ0000000BY9 | ESXXFR | 11-09-2020 |
| “Agrobank” aksiyadorlik tijorat banki | UZ700156K011 | UZ00000004P2 | EPXXXR | 11-09-2020 |
| “Agrobank” aksiyadorlik tijorat banki | UZ7001560000 | UZ00000004Q0 | ESXXFR | 11-09-2020 |
| “Algoritm” aksiyadorlik jamiyati | UZ7043280005 | UZ00000001V6 | ESXXXR | 11-09-2020 |
| “Alliance-Leasing” aksiyadorlik jamiyati | UZ7055590002 | UZ0000000BJ0 | ESXXFR | 11-09-2020 |
opendatauzb::getSecurities() provides a data frame with the list of securities from the Republican Stock Exchange “Toshkent” database
## 5106 securities are found. Out of which are:
## # A tibble: 4 x 2
## # Groups: Type [4]
## Type n
## <chr> <int>
## 1 BND 11
## 2 FCT 492
## 3 RPO 4425
## 4 STK 178
| Type | SecurityCode | Ticker | Issuer |
|---|---|---|---|
| STK | UZ7053260004 | TSMB | <4-sonli Montaj Boshqarmasi> aksiyadorlik jamiyati |
| STK | UZ705326K015 | TSMB1 | <4-sonli Montaj Boshqarmasi> aksiyadorlik jamiyati |
| STK | UZ7045360003 | AGZD | <Agregat zavodi> aksiyadorlik jamiyati |
| STK | UZ7001560000 | AGBA | <Agrobank> aksiyadorlik tijorat banki |
| STK | UZ700156K011 | AGB1 | <Agrobank> aksiyadorlik tijorat banki |
| STK | UZ7055590002 | ALLS | <Alliance-Leasing> aksiyadorlik jamiyati |
| STK | UZ7045320007 | ALSM | <Alskom> sug’urta kompaniyasi aksiyadorlik jamiyati |
| STK | UZ704532K019 | ALS1 | <Alskom> sug’urta kompaniyasi aksiyadorlik jamiyati |
| STK | UZ7047650005 | ABKZ | <Andijon biokimyo zavodi> aksiyadorlik jamiyati |
| STK | UZ7018010007 | ANDD | <Andijon dori-darmon> aksiyadorlik jamiyati |
opendatauzb::currentBidsAsks() returns current bid and ask prices on the Republican Stock Exchange “Toshkent” database.
| Рынок | Площадка | Код ЦБ | Эмитент | Лучшая цена продажи | Общее кол-во ЦБ на продажу | Лучшая цена покупки | Общее кол-во ЦБ на покупку |
|---|---|---|---|---|---|---|---|
opendatauzb::getMarketIndex() downloads Uzbek stock market indices (Uzbekistan Composite Index, UCI) by sectors or all sectors. The sector name should be provided as the following (case-insensitive):
## Uzbekistan Composite Index (Sector: All) will be downloaded for 01.01.2020-12.09.2020
| date | open_price | high_price | low_price | price | previous_day_price | marketcap | trading_volume | trading_value |
|---|---|---|---|---|---|---|---|---|
| 2020-01-03 | 0 | 0 | 0 | 643.15 | 641.97 | 52048360492952.33 | 121303 | 65763290.37 |
| 2020-01-04 | 0 | 0 | 0 | 639.75 | 643.15 | 51714672407463.76 | 1355928 | 106731028.16 |
| 2020-01-06 | 0 | 0 | 0 | 639.45 | 639.75 | 51690319615649.21 | 64672 | 201229179.81 |
| 2020-01-07 | 0 | 0 | 0 | 631.02 | 639.45 | 51003443579354.92 | 1210672 | 204705056.56 |
| 2020-01-08 | 0 | 0 | 0 | 626.86 | 631.02 | 50664174864300.91 | 597241 | 743946494.86 |
| 2020-01-09 | 0 | 0 | 0 | 625.94 | 626.86 | 50593775911126.8 | 311042 | 97060299.18000001 |
| 2020-01-10 | 0 | 0 | 0 | 627.87 | 625.94 | 50751392079610.95 | 3859464 | 261145216.77 |
| 2020-01-13 | 0 | 0 | 0 | 632.62 | 627.87 | 51138187340933.22 | 171658 | 116101857.37 |
| 2020-01-14 | 0 | 0 | 0 | 625.69 | 632.62 | 50709330434036.91 | 154980 | 91653997.59999999 |
| 2020-01-15 | 0 | 0 | 0 | 631.40 | 625.69 | 51174011596455.78 | 338382 | 490836761.29 |
The timeframe can be specified by arguments from and to provided in “%d.%m.%Y” date format:
## Uzbekistan Composite Index (Sector: Finance) will be downloaded for 01.01.2018-30.06.2020
| date | open_price | high_price | low_price | price | previous_day_price | marketcap | trading_volume | trading_value |
|---|---|---|---|---|---|---|---|---|
| 2018-01-04 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 20 | 34000.0 |
| 2018-01-05 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 19537350 | 5079711000.0 |
| 2018-01-06 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 0 | 0.0 |
| 2018-01-08 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 0 | 0.0 |
| 2018-01-09 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 0 | 0.0 |
| 2018-01-10 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 0 | 0.0 |
| 2018-01-11 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 0 | 0.0 |
| 2018-01-12 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 8800000 | 88000000.0 |
| 2018-01-15 | 0 | 0 | 0 | 988.93 | 988.93 | 9527326459319.529 | 77708 | 12044740.0 |
| 2018-01-16 | 0 | 0 | 0 | 988.93 | 988.93 | 9525326119319.529 | 995 | 118405.0 |
opendatauzb::getTicker() obtains stock market prices by security code over the period given:
## New names:
## * `` -> ...9
## * `` -> ...10
## UZ7011340005 has been downloaded for 01.01.2020-12.09.2020
| Date | Closed Price | Change | Trading Volume (shr) | Trading Value (KHR) | Opened | High | Low | symbol |
|---|---|---|---|---|---|---|---|---|
| 2020-01-03 | 36.00 | 0.00 | 84 | 3106.17 | 36.99 | 36.99 | 36.0 | UZ7011340005 |
| 2020-01-04 | 37.00 | 1.00 | 8345 | 309180.00 | 32.00 | 38.00 | 32.0 | UZ7011340005 |
| 2020-01-06 | 36.70 | -0.30 | 122 | 4453.60 | 36.50 | 36.90 | 36.5 | UZ7011340005 |
| 2020-01-07 | 33.00 | -3.70 | 712312 | 23566961.00 | 37.00 | 37.00 | 33.0 | UZ7011340005 |
| 2020-01-08 | 36.50 | 3.50 | 472 | 17040.92 | 36.78 | 36.78 | 30.0 | UZ7011340005 |
| 2020-01-09 | 36.50 | 0.00 | 18146 | 662725.00 | 37.00 | 37.00 | 35.0 | UZ7011340005 |
| 2020-01-10 | 34.99 | -1.51 | 3737160 | 119633828.98 | 33.00 | 34.99 | 32.0 | UZ7011340005 |
| 2020-01-13 | 35.00 | 0.01 | 36026 | 1265501.90 | 36.00 | 36.30 | 35.0 | UZ7011340005 |
| 2020-01-14 | 35.99 | 0.99 | 201 | 7191.20 | 33.00 | 35.99 | 33.0 | UZ7011340005 |
| 2020-01-15 | 35.90 | -0.09 | 3443 | 123610.20 | 36.00 | 36.00 | 35.9 | UZ7011340005 |
| Date | Closed Price | Change | Trading Volume (shr) | Trading Value (KHR) | Opened | High | Low | symbol |
|---|---|---|---|---|---|---|---|---|
| 2018-01-04 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-05 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-06 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-08 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-09 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-10 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-11 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-12 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-15 | 155 | 0 | 57533 | 8917615 | 155 | 155 | 155 | UZ7011340005 |
| 2018-01-16 | 155 | 0 | 0 | 0 | 155 | 155 | 155 | UZ7011340005 |
For this example, we use opendatauzb package’s functions and other packages (quantmod, PerformanceAnalytics, tidyquant) for financial modelling to evaluate the given set of stocks and portfolio performance. we have chosen 20 stocks, including Kvarts, Trustbank, Hamkorbank, UzSQB, ToshkentVino and others.
Using getTicker function, we download stock prices for each of those stocks. Note: if arguments from and to are not provided, by default, both getMarketIndex() and getTicker() functions download stock prices starting from 01.01.2019.
asset_group <- c("UZ704532K019", "UZ7045320007", "UZ7025870005", "UZ7038380000", "UZ7025770007",
"UZ7015030008", "UZ7043200003", "UZ703756K015", "UZ7037560008", "UZ7016400002",
"UZ7004510002", "UZ7023760000", "UZ703348K011", "UZ7033480003", "UZ7035340007",
"UZ7028090007", "UZ701134K017", "UZ7011340005", "UZ701655K011", "UZ7016550004")
assets <- asset_group %>%
lapply(getTicker, from = "01.01.2019", to = "12.08.2020") %>%
bind_rows()Having loaded stock prices, we calculate monthly returns using a tidyquant::tq_transmute() function from tidyquant package. Please refer to the link for more information on using tidyquant package.
As Uzbek Stock Market databases do not provide adjusted closing prices, for this example, we use closing prices. However, in real-world practice, we highly recommend calculating adjusted closing prices.
Ra <- assets %>%
group_by(symbol) %>%
tq_transmute(select = "Closed Price",
mutate_fun = periodReturn,
period = "monthly",
col_rename = "Ra")The output presents only first six rows of calculated monthly returns.
| symbol | Date | Ra |
|---|---|---|
| UZ704532K019 | 2019-01-31 | -0.0029851 |
| UZ704532K019 | 2019-02-28 | 0.0059880 |
| UZ704532K019 | 2019-03-29 | 0.0000000 |
| UZ704532K019 | 2019-04-30 | -0.0059524 |
| UZ704532K019 | 2019-05-31 | -0.0131737 |
| UZ704532K019 | 2019-06-28 | -0.0048544 |
After collecting data for our stocks, we download overall market index as baseline prices for our analysis.
# Baseline prices
Rb <- getMarketIndex("all", from = "01.01.2019", to = "12.08.2020") %>%
tq_transmute(select = price,
mutate_fun = periodReturn,
period = "monthly",
col_rename = "Rb")## Uzbekistan Composite Index (Sector: All) will be downloaded for 01.01.2019-12.08.2020
| date | Rb |
|---|---|
| 2019-01-31 | -0.6157297 |
| 2019-02-28 | 0.0345058 |
| 2019-03-29 | 0.7903746 |
| 2019-04-30 | 0.0249493 |
| 2019-05-31 | -0.0012299 |
| 2019-06-28 | -0.0055492 |
To evaluate if the stock is valued fairly considering associated risks and expected returns, we employ CAPM model. We use tidyquant::tq_performance() function from tidyquant package.
| symbol | ActivePremium | Alpha | Beta | InformationRatio | TrackingError | TreynorRatio |
|---|---|---|---|---|---|---|
| UZ704532K019 | 0.0609 | -0.1578 | 0.0016 | 0.0753 | 0.8091 | -535.6676 |
| UZ7045320007 | 0.0609 | -0.1578 | 0.0016 | 0.0753 | 0.8091 | -535.6676 |
| UZ7025870005 | 1.2321 | -0.1104 | -0.4291 | 0.8328 | 1.4796 | 1.6130 |
| UZ7038380000 | -0.8227 | -0.1539 | 0.5325 | -0.8446 | 0.9741 | NaN |
| UZ7025770007 | -0.1484 | -0.1854 | -0.0782 | -0.1585 | 0.9363 | 11.6321 |
| UZ7015030008 | 0.1465 | -0.0318 | 0.0532 | 0.0833 | 1.7587 | -17.9109 |
| UZ7043200003 | 1.0961 | -0.0922 | -0.0542 | 1.1193 | 0.9793 | 12.8673 |
| UZ703756K015 | 0.1594 | -0.1416 | 0.0582 | 0.2094 | 0.7614 | -14.7441 |
| UZ7037560008 | 0.1594 | -0.1416 | 0.0582 | 0.2094 | 0.7614 | -14.7441 |
| UZ7016400002 | 1.9330 | 0.0536 | 0.5146 | 1.3571 | 1.4244 | -1.0845 |
| UZ7004510002 | -0.0699 | -0.1832 | -0.1227 | -0.0724 | 0.9663 | 7.3111 |
| UZ7023760000 | 9.2425 | 0.3422 | -0.2486 | 2.0382 | 4.5347 | 1.8504 |
| UZ703348K011 | 0.6971 | -0.0746 | -0.0318 | 0.5024 | 1.3877 | 24.7448 |
| UZ7033480003 | 0.6971 | -0.0746 | -0.0318 | 0.5024 | 1.3877 | 24.7448 |
| UZ7035340007 | 25964.5829 | 4.5432 | -0.4637 | 913.9865 | 28.4081 | -1991.5937 |
| UZ7028090007 | 0.8958 | -0.1484 | -0.4853 | 0.6497 | 1.3787 | 1.5272 |
| UZ701134K017 | 0.1397 | -0.1368 | -0.0284 | 0.1270 | 1.0997 | 30.5495 |
| UZ7011340005 | 0.1397 | -0.1368 | -0.0284 | 0.1270 | 1.0997 | 30.5495 |
| UZ701655K011 | 0.1629 | -0.1477 | -0.0736 | 0.1591 | 1.0241 | 11.7079 |
| UZ7016550004 | 0.1629 | -0.1477 | -0.0736 | 0.1591 | 1.0241 | 11.7079 |
We estimate Sharpe ratio for all our stocks to see returns on investment per amount of taken risks. As a rule of thumb, the Sharpe ratio greater than 3 is classified as Excellent, an interval 2.00-2.99 is Very Good, 1.00-1.99 range is Acceptable, the ratio less than 1 is classified as Bad or Sub-optimal.
RaRb %>%
tq_performance(Ra = Ra,
Rb = NULL,
performance_fun = SharpeRatio,
Rf = 0.15) %>%
formattable(list("ESSharpe(Rf=15%,p=95%)" = formatter("span",
style = x ~ ifelse(x >= 3,
style(color = "green",
font.weight = "bold"),
NA))))| symbol | ESSharpe(Rf=15%,p=95%) | StdDevSharpe(Rf=15%,p=95%) | VaRSharpe(Rf=15%,p=95%) |
|---|---|---|---|
| UZ704532K019 | -1.12789189 | -3.75232219 | -1.78127794 |
| UZ7045320007 | -1.12789189 | -3.75232219 | -1.78127794 |
| UZ7025870005 | -0.12947160 | -0.17778980 | -0.14778762 |
| UZ7038380000 | -0.23712348 | -0.79025799 | -0.36825494 |
| UZ7025770007 | -0.69265057 | -1.60678872 | -0.84967804 |
| UZ7015030008 | -0.05814506 | -0.08512571 | -0.07265861 |
| UZ7043200003 | -0.45977881 | -0.58180582 | -0.69660766 |
| UZ703756K015 | -2.07009673 | -3.84148495 | -2.48432885 |
| UZ7037560008 | -2.07009673 | -3.84148495 | -2.48432885 |
| UZ7016400002 | -0.01706879 | -0.04132266 | -0.68890280 |
| UZ7004510002 | -0.84021409 | -1.52712615 | -0.96107323 |
| UZ7023760000 | 0.26727994 | 0.24109435 | 0.34372057 |
| UZ703348K011 | -0.15468606 | -0.21757520 | -0.19196976 |
| UZ7033480003 | -0.15468606 | -0.21757520 | -0.19196976 |
| UZ7035340007 | 3.64833121 | 0.44626039 | 3.64833121 |
| UZ7028090007 | -0.21286472 | -0.35202905 | -0.45154997 |
| UZ701134K017 | -0.39318161 | -0.62773007 | -0.54169233 |
| UZ7011340005 | -0.39318161 | -0.62773007 | -0.54169233 |
| UZ701655K011 | -0.48700581 | -0.84285552 | -0.59469028 |
| UZ7016550004 | -0.48700581 | -0.84285552 | -0.59469028 |
Based on the ratios we calculated above, we choose two stocks for our portfolio: UZ7015030008 (MSBU) and UZ7035340007 (UZKB).
wts <- c(rep(0, 5),
0.35, #6
rep(0, 8),
0.65, #15
rep(0, 5))
portfolio_returns_monthly <- Ra %>%
tq_portfolio(assets_col = symbol,
returns_col = Ra,
weights = wts,
col_rename = "Ra")
left_join(portfolio_returns_monthly,
Rb,
by = c("Date"="date")) %>%
tq_performance(Ra = Ra, Rb = Rb, performance_fun = table.CAPM) %>%
gather() %>%
kable() %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))| key | value |
|---|---|
| ActivePremium | 5.1232 |
| Alpha | 0.6238 |
| AnnualizedAlpha | 334.9753 |
| Beta | 0.2671 |
| Beta- | 0.0067 |
| Beta+ | -0.8533 |
| Correlation | 0.0229 |
| Correlationp-value | 0.9237 |
| InformationRatio | 0.5737 |
| R-squared | 0.0005 |
| TrackingError | 8.9296 |
| TreynorRatio | 15.7192 |
On condition that our initial assumptions hold, let us see how 100,000 UZS investment in these stocks would grow since 1 January 2019.
portfolio_growth_monthly <- Ra %>%
tq_portfolio(assets_col = symbol,
returns_col = Ra,
weights = wts,
col_rename = "investment.growth",
wealth.index = TRUE) %>%
mutate(investment.growth = investment.growth * 100000)As the graph below shows, the initial investment of 100,000 UZS in these 2 stocks in 1 January 2019 is worth around 2.2M UZS as of 30 June 2020 without inflation adjustment.
Note: These estimates are for only illustrative purposes of the practical use of opendatauzb package. The real returns on investment may rely heavily on the assumptions drawn earlier and other exogenous shocks to Uzbek Stock Market.