Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 28

BỘ GIÁO DỤC VÀ ĐÀO TẠO

ĐẠI HỌC KINH TẾ THÀNH PHỐ HỒ CHÍ MINH

TIỂU LUẬN CUỐI KỲ


TÀI CHÍNH ĐỊNH LƯỢNG

XÂY DỰNG DANH MỤC ĐẦU TƯ CÁC CỔ PHIẾU TRONG DANH SÁCH VN30
TRÊN THỊ TRƯỜNG CHỨNG KHOÁN VIỆT NAM: VẬN DỤNG LÝ THUYẾT CỦA
MARKOWITZ VÀ MÔ HÌNH CAPM

Giảng viên hướng dẫn: Vũ Việt Quảng


Lớp học phần: 23C1QUA60503302 (Lớp tối thứ 5)
Sinh viên: Phạm Hồng Phượng
Mã số sinh viên: 522202111232
Email: phuongpham.522202111232@st.ueh.edu.vn
SĐT: 0986931331

TP.HỒ CHÍ MINH - 2023

MỤC LỤC
CHƯƠNG 1: GIỚI THIỆU 3
1.1. Lý do chọn đề tài 3
1.2. Mục tiêu nghiên cứu 3
1.3. Phạm vi nghiên cứu 3
1.4. Phương pháp nghiên cứu 3
1.5. Ý nghĩa nghiên cứu 3
CHƯƠNG 2: CƠ SỞ LÝ THUYẾT 3
2.1. Khái niệm danh mục đầu tư 3
2.2. Quy trình xây dựng danh mục đầu tư chứng khoán 3
2.3. Lý thuyết của Markowitz và lựa chọn danh mục đầu tư chứng khoán 4
2.4. Hệ số Sharpe 5
2.5. Lý thuyết về mô hình CAPM 5
CHƯƠNG 3: ỨNG DỤNG PYTHON XÂY DỰNG DANH MỤC ĐẦU TƯ HIỆU QUẢ TRÊN THỊ
TRƯỜNG CHỨNG KHOÁN VIỆT NAM 6
3.1. Phạm vi xây dựng danh mục 6
3.2. Áp dụng lý thuyết Markowitz mô hình MVP 6
3.3. Mô hình CAPM 20
CHƯƠNG 4: KẾT LUẬN 26
CHƯƠNG 1: GIỚI THIỆU
1.1. Lý do chọn đề tài
Thị trường chứng khoán Việt Nam được đánh giá là một thị trường thứ cấp, thị
trường mới nổi, với phần lớn là các nhà đầu tư trên thị trường là các nhà đầu tư cá nhân
với ít kinh nghiệm, kiến thức chuyên sâu và dễ bị tác động bởi các yếu tố tâm lý. Do đó
các công cụ, mô hình nền tảng giúp phân tích kỹ thuật các chứng khoán trên thị trường để
xây dựng danh mục đầu tư là vô cùng cần thiết đối với các nhà đầu tư hiện nay. Vì vậy,
tác giả lựa chọn thực hiện đề tài tiểu luận: Xây dựng danh mục đầu tư các cổ phiếu trong
danh sách VN30 trên thị trường chứng khoán Việt Nam: Vận dụng lý thuyết của
Markowitz và Mô hình CAPM.
1.2. Mục tiêu nghiên cứu
- Hệ thống lại cơ sở lý thuyết về danh mục đầu tư và xây dựng danh mục đầu tư hiệu
quả
- Ứng dụng Mô hình MVP (Markowitz) và mô hình CAPM để xây dựng danh mục
đầu tư chứng khoán từ các cổ phiếu trong danh VN30 niêm yết trên thị trường chứng
khoán Việt Nam
1.3. Phạm vi nghiên cứu
- Sử dụng dữ liệu là giá cuối ngày của 10 cổ phiếu trong danh mục VN30 và chỉ số thị
trường VNindex thu thập từ Vietstock trong 5 năm từ tháng 01/2018 tới 12/2022, dữ liệu
được lấy theo tuần
1.4. Phương pháp nghiên cứu
- Sử dụng phương pháp thống kê, tổng hợp và phân tích dữ liệu thứ cấp thu thập
được, trong đó xử lý phân tích dữ liệu thông qua Python qua phần mềm Jupyter notebook.
1.5. Ý nghĩa nghiên cứu
- Góp phần xây dựng danh mục đầu tư hiệu quả làm cơ sở tham khảo, góp phần nâng
cao hiệu quả đầu tư cho các nhà đầu tư nhỏ lẻ trên thị trường chứng khoán Việt Nam.

CHƯƠNG 2: CƠ SỞ LÝ THUYẾT
2.1. Khái niệm danh mục đầu tư
Danh mục đầu tư (Investment Portfolio) có thể hiểu đơn giản là sự kết hợp các tài
sản tài chính khác nhau như cổ phiếu, trái phiếu… của một nhà đầu tư. Việc đa dạng hóa
và quản lý danh mục đầu tư hiệu quả là cách làm tốt nhất giúp nhà đầu tư quản trị rủi ro
và tối ưu hóa lợi nhuận khi tham gia thị trường chứng khoán.
2.2. Quy trình xây dựng danh mục đầu tư chứng khoán
Theo Investopedia1, nhà đầu tư có thể xây dựng danh mục đầu tư phù hợp với chiến lược
đầu tư bằng cách thực hiện theo cách tiếp cận có hệ thống như sau:
1 4 steps to building a profitable portfolio. from Investopedia https://www.investopedia.com/financial-advisor/steps-
building-profitable-portfolio/
- Bước 1: Xác định tình hình tài chính và mục tiêu đầu tư, trong đó xác định lợi nhuận
mục tiêu, mức độ chấp nhận rủi ro của nhà đầu tư
- Bước 2: Xác định phân bổ tài sản phù hợp, cần phân chia vốn cho các loại tài sản
thích hợp (cổ phiếu, trái phiếu, chứng khoán phái sinh,…)
- Bước 3: Khi đã thiết lập được danh mục đầu tư, xây dựng các chiến lược để quản lý
danh mục. Thực hiện phân tích và điều chỉnh tỷ lệ giá trị của các khoản đầu tư định kỳ,
vì những biến động giá có thể khiến trọng số đầu tư ban đầu thay đổi.
- Bước 4: Tái cân bằng danh mục, chiến lược quản lý danh mục. Các nhà đầu tư sử
dụng các phương pháp phân tích, chiến lược đầu tư khác nhau để lựa chọn, định giá các
chứng khoán để đa dạng hóa danh mục, giảm thiểu rủi ro (như phương pháp
Fundamental Analysis – phân tích cơ bản; phương pháp Technical Analysis - phân tích
kỹ thuật;...) từ đó đánh giá mức độ hiệu quả của danh mục.
2.3. Lý thuyết của Markowitz và lựa chọn danh mục đầu tư chứng khoán
Mô hình MV – mô hình kỳ vọng phương sai, được phát triển và giới thiệu bởi Harry
Markowitz năm 1952 và được coi là lý thuyết nền tảng trong lựa chọn danh mục đầu tư.
Theo lý thuyết của Markowitz, danh mục đầu tư tối ưu là danh mục đảm bảo giữa 2 mục
tiêu tối đa hóa lợi nhuận hoặc rủi ro tối thiểu.
Trong đó, đường biên hiệu quả: là đường tập hợp các danh mục đầu tư có lợi nhuận
kỳ vọng cao nhất ứng với mỗi một mức rủi ro. Mỗi nhà đầu tư sẽ có một mức độ chấp nhận
rủi ro khác nhau, dẫn tới các mức lợi nhuận kỳ vọng cũng khác nhau.

Đồ thị minh họa đường biên hiệu quả danh mục tài sản rủi ro với trục y là TSSL kỳ
vọng của danh mục P và trục x là độ lệch chuẩn của P
Theo đó, giả sử danh mục có 2 tài sản rủi ro: Cổ phiếu (E) và Trái phiếu (B) để lựa
chọn danh mục đầu tư tối ưu, ta có 2 mô hình như sau:
- Mô hình hàm mục tiêu phương sai nhỏ nhất:

🡪 min
Trong đó:
σ là phương sai của danh mục P
P
2

WE là tỷ trọng đầu tư của cổ phiếu E


WB là tỷ trọng đầu tư của trái phiếu B
σB2 là phương sai của trái phiếu B
σE2 là phương sai của cổ phiếu E
Cov (rB,rE) là hiệp phương sai giữa 2 tài sản
- Mô hình hàm mục tiêu tối đa hóa thu nhập: Với TSSL kỳ vọng của danh mục P:

🡪 max
2.4. Hệ số Sharpe
Hệ số Sharpe, đo lường tỷ lệ phần bù rủi ro trên một đơn rủi ro. Hệ số này giúp nhà đầu
tư cân nhắc khi phải đánh đổi giữa lợi nhuận và rủi ro, nó càng lớn thì cho thấy danh mục
đầu tư càng hiệu quả (tỷ trọng đầu tư tối ưu). Hệ số Sharpe của một danh mục đầu tư được
tính bằng công thức:

2.5. Lý thuyết về mô hình CAPM


Mô hình CAPM là mô hình định giá tài sản vốn được phát triển bởi William Sharpe,
được sử dụng để tính toán tỷ suất sinh lời kỳ vọng của nhà đầu tư cho một tài sản hoặc
khoản đầu tư. CAPM thực hiện điều này bằng cách sử dụng lợi nhuận kỳ vọng trên cả thị
trường và tài sản phi rủi ro cũng như mối tương quan hoặc độ nhạy cảm của tài sản đó với
thị trường (beta thị trường).
Theo đó, TSSL kỳ vọng theo mô hình CAPM được tính toán bằng công thức:
Có hiểu rằng, danh mục đầu tư được kết hợp giữa các tài sản rủi ro và tài sản phi rủi ro
sẽ đem lại lợi nhuận kỳ vọng bằng lợi nhuận phi rủi ro cộng thêm với phần bù rủi ro. Trong
đó phần bù là phần lợi nhuận kỳ vọng tăng thêm so với lợi nhuận phi rủi ro.

CHƯƠNG 3: ỨNG DỤNG PYTHON XÂY DỰNG DANH MỤC ĐẦU TƯ HIỆU
QUẢ TRÊN THỊ TRƯỜNG CHỨNG KHOÁN VIỆT NAM
3.1. Phạm vi xây dựng danh mục
Xây dựng danh mục căn cứ vào dữ liệu quá khứ, giá cổ phiếu theo các tuần từ tháng 01/
2018 tới tháng 12/2022. Lựa chọn danh mục gồm 10 cổ phiếu từ VN30 bao gồm: ACB
(Ngân hàng Thương mại Cổ phần Á châu), BID (Ngân hàng TMCP Đầu tư và Phát triển Việt
Nam), FPT (Công ty Cổ phần FPT), HDB (Ngân hàng TMCP Phát triển T.P Hồ Chí Minh),
HPG (CTCP Tập đoàn Hòa Phát), MBB (Ngân hàng Thương mại cổ phần Quân đội), PLX
(Tập đoàn Xăng dầu Việt Nam), SAB (CTCP Bia - Rượu - Nước giải khát Sài Gòn), SSI
(CTCP Chứng khoán SSI), VIC (Tập đoàn VINGROUP); và dữ liệu chỉ số VNI (chỉ số thị
trường Vnindex) và sử dụng Python để tính toán, xây dựng danh mục theo lý thuyết
Markowitz và mô hình CAPM.
3.2. Áp dụng lý thuyết Markowitz mô hình MVP
- Để có thể sử dụng Python, đầu tiên thực hiện input khai báo như sau:
Input [1]: import numpy as np
import pandas as pd
from pylab import plt, mpl
from scipy.optimize import minimize
plt.style.use('seaborn-v0_8')
mpl.rcParams['savefig.dpi']=300
mpl.rcParams['font.family'] = 'serif'
np.set_printoptions(precision=5, suppress=True,
formatter={'float': lambda x: f'{x:6.3f}'})

Giải thích ý nghĩa: nhập (import) các thư viện, các thông tin kiểu vẽ đồ thị và cấu hình tùy
chọn cho việc in ra kết quả. Cụ thể:
import numpy as np: nhập thư viện numpy với ký hiệu là np;
import pandas as pd: thư viện pandas với ký hiệu là pd;
from pylab import plt, mpl: từ module pylab nhập chức năng vẽ đồ thị plt và mpl
(matplotlib)
from scipy.optimize import minimize: Nhập hàm minimize từ module tối ưu hóa
(optimize) của SciPy. Để thực hiện tìm giá trị nhỏ nhất
plt.style.use('seaborn-v0_8'): khai báo kiểu vẽ đồ thị từ chức năng plt của
numPy, là kiểu seaborn bản v0_8
mpl.rcParams['savefig.dpi']=300: thiết lập độ phân giải hiển thị cho đồ thị là 300
điểm (trên mỗi inchs hiển thị) để đảm bảo chất lượng hình ảnh
mpl.rcParams['font.family'] = 'serif':thiết lập font chữ sử dụng cho biểu đồ là
serif
np.set_printoptions(precision=5,suppress=True,formatter={'float': lambda
x: f'{x:6): khai báo cho cấu hình tùy chọn in của NumPy, trong đó precision =5 nghĩa là
kết quả đầu ra có mức độ chính xác đến 5 chữ số thập phân; suppress = True: ẩn các số lẻ;
formatter: độ dài trường in ra là 6 ký tự, lấy sau dấu phẩy 3 chữ số thập phân.
- Sử dụng code Python để truy xuất tệp dữ liệu:
Input [2]: url='Du lieu VN30 va VNI.csv'
Giải thích ý nghĩa: tạo đối tượng url để chứa tệp dữ liệu (CSV) giá đóng cửa của 10 cổ
phiếu và giá đóng của của chỉ số Vnindex (đã được đưa lên Jupyter notebook)

Input [3]: raw = pd.read_csv(url, index_col=0, parse_dates=True).dropna()

Giải thích ý nghĩa: tạo đối tượng raw là một DataFrame, kết quả của hàm pd.read_csv, đọc
dữ liệu từ tệp url đã nhập liệu, trong đó: index_col =0 để chỉ định cột đầu tiên của tệp dữ liệu
dùng để làm chỉ mục; parse_dates = True để chỉ định các giá trị trong cột chỉ mục được đổi
sang kiểu dữ liệu dates (ngày tháng năm) và dùng lệnh dropna() để xóa bỏ các hàng chứa giá
trị trống.
Input [4]: raw.info()
Output[4]: <class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 259 entries, 2018-01-05 to 2022-12-30
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 ACB 259 non-null int64
1 BID 259 non-null int64
2 FPT 259 non-null int64
3 HDB 259 non-null int64
4 HPG 259 non-null int64
5 MBB 259 non-null int64
6 PLX 259 non-null int64
7 SAB 259 non-null int64
8 SSI 259 non-null int64
9 VIC 259 non-null int64
10 VNI 259 non-null float64
dtypes: float64(1), int64(10)
memory usage: 24.3 KB

Giải thích ý nghĩa: Sử dụng hàm info trong thư viện Pandas để xem thông tin của đối
tượng raw vừa tạo được. Ta nhận được kết quả thông tin đối tượng raw như sau:
- Đây là một DataFrame với 259 dòng và 11 cột;
- Chỉ mục của DataFrame này có dạng DatetimeIndex với 259 chỉ mục bắt đầu từ
ngày 2018-01-05 tới ngày 2022-12-30.
- Các cột của DataFrame này là 10 mã chứng khoán: ACB, BID, FPT, HDB, HPG,
MBB, PLX, SAB, SSI, VIC;
- Kiểu dữ liệu là int64 ; và mất 22.3 KB để lưu trữ tệp dữ liệu này.
Input [5]: symbols = ['ACB','BID',
'FPT','HDB','HPG','MBB','PLX','SAB','SSI','VIC']

Giải thích ý nghĩa: tạo đối tượng symbols là 1 list 10 mã cổ phiếu, để sử dụng cho các phân
tích, tính toán phía sau.
- Tính Tỷ suất lợi nhuận của các cổ phiếu: Tỷ suất sinh lời hàng ngày (log returns) là
một phép đo hiệu suất của một khoản đầu tư trong một ngày. Nó được tính bằng cách lấy
logarit tự nhiên của giá trị của khoản đầu tư tại thời điểm hiện tại chia cho giá trị của khoản
đầu tư tại thời điểm trước đó. Ta thực hiện:
Input [6]: rets = np.log(raw[symbols] / raw[symbols].shift(1)).dropna()

Giải thích ý nghĩa: tạo đối tượng rets, trong đó lưu kết quả là 1 DataFrame có các cột chứa
kết quả của hàm np.log tính toán logarit của các giá trị lấy từ:
raw[symbols]: các giá trị của các cột trong đối tượng raw tương ứng với các mã cổ phiếu
trong symbols; chia cho
raw[symbols].shift(1): các giá trị của các cột trong đối tượng raw tương ứng với các mã
cổ phiếu trong symbols tại thời điểm trước đó; và sau đó dùng lệnh
dropna() loại bỏ các dòng có giá trị thiếu (NaN).

Input [7]: (raw[symbols] / raw[symbols].iloc[0]).plot(figsize=(10, 6));


Giải thích ý nghĩa: đoạn code này dùng để vẽ biểu đồ thể hiện sự thay đổi giá trị theo thời
gian của các mã cổ phiếu trong danh sách symbols, trong đó các dữ liệu đã được chuẩn hóa.
Cụ thể:
- raw[symbols] / raw[symbols].iloc[0]): Thực hiện phép chia giá trị của mỗi
phần tử trong các cột raw cho giá trị đầu tiên tương ứng của nó (dùng iloc[0] để
lấy giá trị đầu tiên của mỗi cột trong các cột đã chọn) . Kết quả là giá trị chuẩn
hóa, cho thấy sự thay đổi tương đối của giá trị cổ phiếu so với thời điểm đầu tiên.
- plot(figsize=(10, 6)): sau đó thực hiện vẽ biểu đồ với các giá trị cổ phiếu với
kích cỡ biểu đồ là chiều ngang 10 chiều dọc 6 đơn vị.
- Để đạt được mục tiêu đặt đường cơ sở cho hiệu suất được đưa ra bởi danh mục đầu tư
có tỷ trọng các tài sản như nhau trong toàn bộ khoảng thời gian, ta thực hiện các code
Python sau đây để xác định các hàm để tính toán lợi nhuận của danh mục đầu tư, độ biến
động của danh mục đầu tư và hệ số Sharpe của danh mục đầu tư:
Input [8]: weights = len(rets.columns) * [1 / len(rets.columns)]

Giải thích ý nghĩa: tạo đối tượng weights -1 vector là kết quả tính các trong số cân bằng
nhau, trong đó mỗi trọng số có giá trị bằng 1/ số lượng cột trong DataFrame rets. Điều này
đảm bảo được tỷ trọng sẽ cân bằng nhau. Cụ thể:
- len(rets.columns): Dùng hàm len để đếm số lượng cột trong rets và trả về kết
quả là một số nguyên. Với rets có 10 cột, kết quả sẽ là 10.
- [1 / len(rets.columns)]: danh sách này sẽ chứa 1 phần tử duy nhất là giá trị 1/
số lượng cột đếm được trong rets. Kết quả sẽ là 1/10 =0.1
- Nhân danh sách 0.1 với số lượng cột là 10. Kết quả nhận được là weights gồm 1
dòng và 10 cột.

Input [9]: def port_return(rets, weights):


return np.dot(rets.mean(), weights) * 52

Giải thích ý nghĩa: đoạn code này là để khai báo công thức của hàm tính lợi nhuận kỳ vọng,
trong đó:
- def port_return(rets, weights): dùng hàm def định nghĩa cho hàm lợi nhuận
kỳ vọng là port_return với các argument được đưa vào hàm là rets: chứa tỷ
suất sinh lợi hàng ngày của các cổ phiếu, weights: chứa trọng số tương ứng với
các tài sản trong danh mục đầu tư.
- return np.dot(rets.mean(), weights) * 52: dùng hàm return để trả kết quả
của công thức tính là tích vô hướng (sử dụng np.dot) giữa mảng giá trị trung bình
cộng của rets và mảng weights; * 52 là để chuyển đổi dữ liệu lợi nhuận hàng
ngày thành hàng năm (do dữ liệu được thu thập theo tuần, 1 năm giả định có 52
tuần).
Như vậy, tóm lại đoạn code khi triển khai công thức của hàm port_return sẽ chạy các bước
như sau:
- Nhận dữ liệu từ tỷ suất lợi nhuận (rets) và các trọng số (weights)
- Tính trung bình cộng của tỷ suất lợi nhuận hàng ngày cho mỗi tài sản.
- Thực hiện tích vô hướng giữa vector trung bình và vector trọng số để tính lợi
nhuận kỳ vọng hàng ngày.
- Nhân lợi nhuận kỳ vọng hàng ngày với 52 để chuyển đổi sang lợi nhuận kỳ vọng
hàng năm.
- Trả về kết quả lợi nhuận kỳ vọng hàng năm.
Input 10 sẽ chạy kết quả của công thức này:
Input [10]: port_return(rets, weights)

Output[10] -0.09182403043677012

Giải thích ý nghĩa: Điều này có nghĩa là kết quả tỷ suất sinh lời kỳ vọng của danh mục này
hàng năm là -9,18%
Input [11]: def port_volatility(rets, weights):
return np.dot(weights, np.dot(rets.cov() * 52 , weights))
** 0.5

Giải thích ý nghĩa: đoạn code này để nhập công thức tính toán độ biến động của danh mục
10 cổ phiếu. Trong đó:
- def port_volatility(rets, weights): dùng hàm def để định nghĩa cho hàm
độ biến động của danh mục port_volatility. Trong đó, đầu vào của hàm là
rets: chứa tỷ suất sinh lợi hàng ngày của các cổ phiếu, weights: chứa trọng số
tương ứng với các tài sản trong danh mục đầu tư.
- return np.dot(weights, np.dot(rets.cov() * 52 , weights)):dùng hàm
return để trả kết quả tính toán phương sai của danh mục, đo lường mức độ biến
động dự kiến của lợi nhuận danh mục so với mức trung bình của nó. Nhân tích vô
hướng của
o np.dot(rets.cov() * 52: mảng kết quả tính toán ma trận hiệp phương
sai của rets theo năm; với
o trọng số trong mảng weights;
- **0.5 là để lấy căn bậc hai của phương sai để tính độ lệch chuẩn - thước đo độ
biến động
Như vậy, tóm lại đoạn code khi triển khai công thức của hàm port_volatility sẽ chạy các
bước như sau:
- Nhận dữ liệu từ tỷ suất lợi nhuận (rets) và các trọng số (weights)
- Tính ma trận hiệp phương sai của danh mục và nhân với 52 để chuyển đổi sang
theo năm.
- Thực hiện tích vô hướng giữa vector hiệp phương sai và vector trọng số lấy căn
bậc hai để tính độ lệch chuẩn – biến động của lợi nhuận
- Trả về kết quả tính độ biến động của lợi nhuận danh mục 10 cổ phiếu.
Input 12 sẽ chạy kết quả của công thức này:
Input [12]: port_volatility(rets, weights)

Output[12]: 0.26253260925188804
Giải thích ý nghĩa: Điều này có nghĩa là kết quả độ lệch chuẩn của danh mục này hàng năm
là 26,3%
Input [13]: def port_sharpe(rets, weights):
return port_return(rets, weights) /
port_volatility(rets, weights)

Giải thích ý nghĩa: đoạn code này để nhập công thức tính toán hệ số Sharpe của danh mục.
Trong đó:
- def port_sharpe(rets, weights)dùng hàm def để định nghĩa cho hàm hệ số
Sharpe port_sharpe. Trong đó, đầu vào của hàm là rets: chứa tỷ suất sinh lợi
hàng ngày của các cổ phiếu, weights: chứa trọng số tương ứng với các tài sản
trong danh mục đầu tư.
- return port_return(rets, weights) / port_volatility(rets,
weights):dùng hàm return để trả kết quả cho phép chia của tỷ suất lợi nhuận
hàm port_return(rets, weights) và độ biến động hay độ lệch chuẩn của danh
mục port_volatility(rets, weights)
Input 14 sẽ chạy kết quả của công thức này:
Input [14]: port_sharpe(rets, weights)

Output[14]: -0.34976238075121996
Giải thích ý nghĩa: Điều này có nghĩa là kết quả hệ số Sharpe của danh mục này hàng năm
là -34,9%. Cho thấy, danh mục này không hiệu quả trong việc tạo ra lợi nhuận so với rủi ro
mà nó mang lại.
- Để phân tích và biểu diễn những kết hợp giữa rủi ro và lợi nhuận danh mục đầu tư,
thực hiện áp dụng mô phỏng Monte Carlo để ngẫu nhiên hóa trọng số của danh mục đầu tư,
thể hiện tỷ lệ Sharpe của danh mục. Ta thực hiện đoạn code Python sau:

Input [15]: w = np.random.random((1000, len(symbols)))


w = (w.T / w.sum(axis=1)).T

Giải thích ý nghĩa: thực hiện mô phỏng trọng số danh mục đầu tư cộng lên tới 100%. Cụ
thể
- w = np.random.random((1000, len(symbols))): tạo đối tượng w là một ma trận
ngẫu nhiên có kích thước 1000 hàng và số cột bằng số ký tự trong symbols là 10
cột.
- w = (w.T / w.sum(axis=1)).T: dùng lệnh Transpose để chuyển hàng thành
cột của ma trận w và ngược lại, Sau đó, ma trận này được chuẩn hóa để tổng
các phần tử trên mỗi hàng bằng 1 (100%).
Input 16 sẽ truy xuất 5 dòng đầu tiên của đối tượng w
Input [16]: w[:5]
Output[16]: array([[ 0.088, 0.105, 0.089, 0.143, 0.145, 0.196, 0.036,
0.021, 0.106, 0.070],
[ 0.172, 0.020, 0.178, 0.156, 0.061, 0.166, 0.103,
0.015, 0.120, 0.010],
[ 0.168, 0.131, 0.121, 0.200, 0.012, 0.076, 0.163,
0.006, 0.004, 0.120],
[ 0.081, 0.021, 0.115, 0.124, 0.153, 0.003, 0.144,
0.106, 0.090, 0.163],
[ 0.035, 0.095, 0.037, 0.100, 0.170, 0.126, 0.112,
0.090, 0.080, 0.155]])

Input [17]: pvr = [(port_volatility(rets[symbols], weights),


port_return(rets[symbols], weights))
for weights in w]
pvr = np.array(pvr)

Giải thích ý nghĩa:


pvr=[(port_volatility(rets[symbols],weights),port_return(rets[symbols],
weights)): để tạo đối tượng pvr là 1 list các cặp giá trị của hàm port_volatility và
port_return hay cặp giá trị tỷ lệ biến động và lợi nhuận của các danh mục cổ phiếu đã được
lưu trong symbols. Sau đó dùng lệnh for weights in w để chạy vòng lặp cho weights qua
mỗi tập hợp trọng số trong mảng w, chứa các trọng số ngẫu nhiên để tạo 1000 danh mục.
Sau đó, pvr = np.array(pvr): chuyển đối tượng pvr là 1 list thành mảng numpy pvr là 1
vector 2 chiều.
Input [18]: psr = pvr[:, 1] / pvr[:, 0]

Giải thích ý nghĩa: mục đích của input này là để tính toán hệ số Sharpe của mỗi danh
mục đầu tư trong mảng pvr và lưu trữ kết quả trong đối tượng tên là psr. Cụ thể như sau:
lấy tất cả các phần tử cột thứ 2 của pvr chứa các giá trị tỷ suất sinh lời kỳ vọng của danh
mục đầu tư chia cho các phần tử của cột đầu tiên của pvr chứa các độ lệch chuẩn của danh
mục đầu tư.
- Với kết quả tính tỷ lệ Sharpe tại input 18, ta thực hiện vẽ biểu đồ biểu diễn sự phân
tán thể hiện sự đánh đổi rủi ro-lợi nhuận của các danh mục đầu tư như sau:
Input [19]: plt.figure(figsize=(10, 6))
fig = plt.scatter(pvr[:, 0], pvr[:, 1],
c=psr, cmap='coolwarm')
cb = plt.colorbar(fig)
cb.set_label('Sharpe ratio')
plt.xlabel('expected volatility')
plt.ylabel('expected return')
plt.title(' | '.join(symbols));

Giải thích ý nghĩa:


Vẽ biểu đồ với trục x thể hiện độ lệch chuẩn (rủi ro) dự kiến (expected volatility) của
danh mục đầu tư và trục y thể hiện tỷ suất sinh lời dự kiến (expected return) của danh mục
đầu tư. Mỗi điểm trên biểu đồ đại diện cho một danh mục đầu tư cụ thể. Màu sắc của mỗi
điểm thể hiện tỷ lệ Sharpe của danh mục đầu tư đó. Cụ thể, thực hiện các bước sau:
- plt.figure(figsize=(10, 6)): tạo biểu đồ mới với kích thước 10 x 6 đơn vị
- fig=plt.scatter(pvr[:,0],pvr[:,1],c=psr,cmap='coolwarm'): dùng lệnh scatter
của matplotlib để tạo một biểu đồ độ phân tán với các giá trị pvr[:,0] là các phần tử
của cột đầu tiên trong pvr - độ lệch chuẩn dự kiến của danh mục đầu tư; pvr[:,1] là
các phần tử của cột thứ hai trong pvr thể hiện các tỷ suất lợi nhuận dự kiến; c=psr để
tô màu cho các điểm tỷ lệ Sharpe của danh mục đầu tư, cmap='coolwarm': để sử dụng
bản đồ đổ màu từ xanh lam đến đỏ phụ thuộc vào tỷ lệ Sharpe cao hay thấp (xanh là tỷ
lehe thấp và đỏ là tỷ lệ Sharpe cao.
- cb=plt.colorbar(fig)
cb.set_label('Sharpe ratio'): đoạn mã Python này để thêm thanh màu vào bên
phải của biểu đồ để thể hiện mức độ của tỷ lệ Sharpe. Và đặt tên cho thanh biểu diễn là
Sharpe ratio
- plt.xlabel('expected volatility')
plt.ylabel('expected return')
plt.title(' | '.join(symbols)):
đoạn mã Python này để thêm label cho trục x là expected volatility – độ biến động dự
kiến; trục y là expected return là tỷ suất sinh lời kỳ vọng; thêm title cho các mã cổ
phiếu trong symbols và ngăn cách bởi ký hiệu |

Biểu đồ phân tán này cho thấy các danh mục đầu tư với độ biến động dự kiến cao hơn
sẽ đem lại lợi nhuận cao hơn, phù hợp với lý thuyết Markowitz “high risk – high return”.
Các nhà đầu tư có thể sử dụng biểu đồ này để phân tích, lựa chọn các danh mục đầu tư có sự
kết hợp các tài sản, cổ phiếu phù hợp với khẩu vị rủi ro cũng như mục tiêu của mình.
Input [18]: psr = pvr[:, 1] / pvr[:, 0]

- Để xem xét lựa chọn danh mục tối ưu, với tỷ lệ Sharpe là tối đa, ta thực hiện các đoạn
mã Python sau đây để tính toán, phân tích:
Input [20]: bnds = len(symbols) * [(0, 1),]
bnds

Output[20]:
[(0, 1),
(0, 1),
(0, 1),
(0, 1),
(0, 1),
(0, 1),
(0, 1),
(0, 1),
(0, 1),
(0, 1)]

Giải thích ý nghĩa: tạo đối tượng bnds là chỉ định giới hạn cho trọng số cho các tài sản
trong danh mục đầu tư, mỗi một phần tử chạy trong phạm vi từ 0 đến 1, với số phần tử bằng
độ dài của đối tượng symbols. Sau đó truy xuất bnds.

Input [21]: cons = {'type': 'eq', 'fun': lambda weights: weights.sum() - 1}

Giải thích ý nghĩa: mã Python này để tạo đối tượng cons lưu trữ một ràng buộc ràng đối với
đối tượng weights - trọng số đầu tư của danh mục sao cho chúng tổng của chúng bằng 1.
Điều này để đảm bảo tổng vốn đầu tư được phân bổ cho mỗi tài sản, cổ phiếu trong danh
mục, nếu tổng các trọng số không bằng 1 thì vốn đầu tư không được phân bổ đầy đủ.

Input [22]: opt_weights = {}


for year in range(2018, 2022):
rets_ = rets[symbols].loc[f'{year}-01-01':f'{year}-12-31']
ow = minimize(lambda weights: -port_sharpe(rets_, weights),
len(symbols) * [1 / len(symbols)],
bounds=bnds,
constraints=cons)['x']
opt_weights[year] = ow

Giải thích ý nghĩa: đoạn code này thực hiện tính tỷ trọng tối ưu đế tối đa hóa hệ số Sharpe
tìm ra danh mục tối ưu. Cụ thể thực hiện các bước như sau:
- opt_weights = {}: tạo một từ điển opt_weights trống để lưu trữ kết quả tính tỷ
trọng tối ưu
- for year in range(2018, 2022): chạy vòng lặp từ năm 2018 tới năm 2021 với
các đối tượng:
● rets_ = rets[symbols].loc[f'{year}-01-01':f'{year}-12-31']: tạo
đối tượng rets_ lưu trữ kết quả tham chiếu dữ liệu từ các cột của symbols
trong rets, trong đó sử dụng loc để giới hạn lấy các hàng trong khoảng
thời gian từ ngày 01/01 đến ngày 31/12 theo biến year. Kết quả được lưu
trong đối tượng rets . Sau đó, nó sử dụng phương thức loc để lấy các hàng
trong khoảng thời gian từ ngày đầu tiên của năm đến ngày cuối cùng của
năm được chỉ định bởi biến year.
● ow = minimize(lambda weights: -port_sharpe(rets_, weights),
len(symbols) * [1 / len(symbols)],
bounds=bnds,
constraints=cons)['x'] : tạo đối tượng ow lưu trữ
kết quả của phép tính tỷ trọng đầu tư tối ưu xác định bằng hàm minimize
với:
lambda weights: -port_sharpe(rets_, weights): Hàm mục tiêu cần tối
thiểu hóa (âm tỷ lệ Sharpe nghĩa là Sharpe sẽ là tối đa); len(symbols) * [1
/ len(symbols)]: Điểm bắt đầu cho quá trình tối ưu hóa (trọng số bằng
nhau cho các tài sản); bounds=bnds: Các giới hạn ràng buộc cho các trọng
số là đối tượng bnds đã khai báo; constraints=cons: Ràng buộc tổng trọng
số bằng 1 đã khai báo; ['x']: Lấy ra kết quả tối ưu (các trọng số tối ưu).
● opt_weights[year] = ow: để lưu trữ kết quả trọng số tối ưu vào đối tượng
opt_weights

Sau khi đoạn code này chạy xong, đối tượng opt_weights sẽ chứa một từ điển với các
năm từ 2018 đến 2022 và các giá trị là các mảng chứa các trọng số tối ưu cho các tài sản
trong các năm tương ứng. Các trọng số này sẽ được sử dụng để xây dựng các danh mục đầu
tư tối ưu cho từng năm. Kết quả được thể hiện trong input/output 23:

Input [23]: opt_weights


Output[23]: {2018: array([ 0.000, 0.499, 0.000, 0.000, 0.000, 0.000,
0.000, 0.000,
0.000, 0.501]),
2019: array([ 0.000, 0.171, 0.515, 0.000, 0.000, 0.000,
0.000, 0.000,
0.000, 0.314]),
2020: array([ 0.000, 0.000, 0.000, 0.000, 0.454, 0.000,
0.000, 0.000,
0.546, 0.000]),
2021: array([ 0.027, 0.000, 0.838, 0.000, 0.000, 0.000,
0.000, 0.000,
0.135, 0.000])}

- Tiếp theo, ta thực hiện so sánh số liệu thống kê danh mục dự kiến với số liệu thống kê
danh mục đầu tư đã thực hiện cho năm hiện tại – số liệu thực tế, để xem xét, so sánh các giá
trị dự kiến theo lý thuyết Markowitz với giá trị thực tế ghi nhận vào năm sau, từ đó đánh giá
xem so với thực tế, dự báo chính xác nhiều hay ít. Cụ thể thực hiện như sau:

Input [24]: res = pd.DataFrame()


for year in range(2018,2022):
rets_ = rets[symbols].loc[f'{year}-01-01':f'{year}-12-
31']
epv = port_volatility(rets_, opt_weights[year])
epr = port_return(rets_, opt_weights[year])
esr = epr / epv
rets_ = rets[symbols].loc[f'{year + 1}-01-01':f'{year +
1}-12-31']
rpv = port_volatility(rets_, opt_weights[year])
rpr = port_return(rets_, opt_weights[year])
rsr = rpr / rpv
res = pd.concat([res, pd.DataFrame({'epv': epv, 'epr':
epr, 'esr': esr,'rpv': rpv, 'rpr': rpr, 'rsr': rsr},
index=[year + 1])])

Giải thích ý nghĩa: mục đích của mã Python là để tính toán và tổng hợp các chỉ số thống kê
của danh mục đầu tư tối ưu trong các năm từ 2018 đến 2022 bao gồm:
- expected volatility – epv: độ biến động dự kiến;
- expected return – epr: TSSL dự kiến
- expected Sharpe ratio – esr: tỷ lệ Sharpe dự kiến
- realized volatility – rpv: độ biến động thực tế
- realized return – rpr: TSSL thực tế
- realized Sharpe ratio – rsr: tỷ lệ Sharpe thực tế
Cụ thể từng bước triển khai như sau:
- res = pd.DataFrame(): tạo một data Frame res trống để lưu trữ kết quả tính toán.
- for year in range(2018,2022): chạy vòng lặp từ năm 2018 tới năm 2021 với
các đối tượng:
● rets_ = rets[symbols].loc[f'{year}-01-01':f'{year}-12-31']: đoạn
mã này để thực hiện tham chiếu dữ liệu của năm hiện tại, tương tự với
input 22 tạo đối tượng rets_ lưu trữ kết quả lấy dữ liệu từ các cột của
symbols trong rets, trong đó sử dụng loc để giới hạn lấy các hàng trong
khoảng thời gian từ ngày 01/01 đến ngày 31/12 theo biến year (năm hiện
tại). Kết quả được lưu trong đối tượng rets. Sau đó, nó sử dụng phương
thức loc để lấy các hàng trong khoảng thời gian từ ngày đầu tiên của năm
đến ngày cuối cùng của năm được chỉ định bởi biến year (năm hiện tại).
● Sử dụng hàm port_volatility và port_return và dữ liệu từ rets_ cùng
với trọng số tối ưu của năm đó từ từ điển opt_weights để tính toán độ biến
động danh mục đầu tư trong năm (epv), lợi nhuận (epr).
● esr = epr / epv : tính tỷ lệ Sharpe dự kiến được tính toán bằng cách lấy
epr chia cho epv
● rets_ = rets[symbols].loc[f'{year + 1}-01-01':f'{year + 1}-12-
31']: tương tự thực hiện tham chiếu dữ liệu lợi nhuận của năm tiếp theo
(year+1) từ rets với các điều kiện tương tự.
● Sử dụng hàm port_volatility và port_return và dữ liệu từ rets_ cùng
với trọng số tối ưu của năm đó từ từ điển opt_weights để tính toán độ biến
động danh mục đầu tư trong năm (rpv), lợi nhuận (rpr).
● rsr = rpr / rpv: tính tỷ lệ Sharpe thực tế (rsr) được tính toán bằng cách
lấy rpr chia cho rpv
● res = pd.concat([res, pd.DataFrame({'epv': epv, 'epr': epr,
'esr': esr,'rpv': rpv, 'rpr':
rpr, 'rsr': rsr},
index =[year + 1])]): sử
dụng hàm concat trong thư viện Pandas để nối thêm một DataFrame
mới vào DataFrame res. DataFrame này sẽ có index là năm tiếp theo
year+1 và các cột là các chỉ số danh mục đầu tư epv, epr, esr, rpr, rpv, rsr.

Input 25 sẽ hiển thị kết quả của DataFrame res:

Input [25]: res


Output[25]: epv epr esr rpv rpr rsr

2019 0.362510 0.205161 0.565945 0.169198 0.246944 1.459496


2020 0.148001 0.275497 1.861449 0.251253 -0.000268 -0.001068
2021 0.412817 0.575185 1.393315 0.347598 0.295699 0.850690
2022 0.286489 0.445871 1.556325 0.355344 -0.322437 -0.907394

- Để quan sát rõ hơn, ta thực hiện vẽ biểu đồ để so sánh giữa giá trị thực tế và giá trị dự
báo. Đầu tiên, thực hiện tính giá trị trung bình các chỉ số của danh mục đầu tư đã lưu trong
res, kết quả thu được là:
Input [26]: res.mean()
Output[26]: epv 0.302455
epr 0.375428
esr 1.344259
rpv 0.280848
rpr 0.054984
rsr 0.350431
dtype: float64

- Thự c hiện tính toán các hệ số tương quan giữa từng cặp giá trị và vẽ biểu đồ thể
hiện :epv-rpv; epr-rpr; esr-rsr:

Input [27]: res[['epv', 'rpv']].corr()


Output[27]: epv rpv
epv 1.000000 0.132481
rpv 0.132481 1.000000

Giải thích ý nghĩa: sử dụng hàm corr của Python để tính hệ số tương quan giữa 2 cột epv
và rpv. Kết quả cho thấy 2 biến này có hệ số tương quan là 13,2% tương đối yếu.

Thực hiện vẽ biểu đồ dạng thanh cột với kích thước là 10x6 và khai báo title cho bảng là
'Expected vs. Realized Portfolio Volatility'

Input [28]: res[['epv', 'rpv']].plot(kind='bar', figsize=(10, 6),


title='Expected vs. Realized Portfolio Volatility');
Từ biểu đồ ta có thể thấy, khi so sánh tỷ suất lợi nhuận dự kiến theo lý thuyết Markowitz với
thực tế tuy tương quan cùng chiều nhưng mức độ chênh lệch vẫn khá lớn.

Input [29]: res[['epr', 'rpr']].corr()


Output[29]: epr rpr
epr 1.000000 -0.039914
rpr -0.039914 1.000000

Giải thích ý nghĩa: sử dụng hàm corr của Python để tính hệ số tương quan giữa 2 cột epr và
rpr. Kết quả cho thấy 2 biến này có hệ số tương quan là - 4% là yếu.

Thực hiện vẽ biểu đồ dạng thanh cột với kích thước là 10x6 và khai báo title cho bảng là
'Expected vs. Realized Portfolio Return'

Input [30]: res[['epr', 'rpr']].plot(kind='bar', figsize=(10, 6),


title='Expected vs. Realized Portfolio Return');
Từ biểu đồ ta có thể thấy được, chỉ có năm 2019 và năm 2022, hai biến epr và rpr có tương
quan cùng chiều. Năm 2020 và 2022, độ biến động dự kiến so với thực tế hoàn toàn trái
ngược nhau. Hay có thể hiểu là việc dự báo hoàn toàn không đúng so với thực tế.

Input [31]: res[['esr', 'rsr']].corr()


Output[31]: esr rsr
esr 1.000000 -0.751587
rsr -0.751587 1.000000

Giải thích ý nghĩa: sử dụng hàm corr của Python để tính hệ số tương quan giữa 2 cột esr và
rsr. Kết quả cho thấy 2 biến này có hệ số tương quan là - 75% là rất yếu.

Thực hiện vẽ biểu đồ dạng thanh cột với kích thước là 10x6 và khai báo title cho bảng là
'Expected vs. Realized Sharpe Ratio'
Input [32]: res[['esr', 'rsr']].plot(kind='bar', figsize=(10, 6),
title='Expected vs. Realized Sharpe Ratio');
Từ biểu đồ ta có thể thấy được, chỉ có năm 2019 và năm 2022, hai biến esr và rsr có tương
quan cùng chiều nhưng độ chênh lệch là khá lớn. Năm 2020 và 2022, độ biến động dự kiến
so với thực tế hoàn toàn trái ngược nhau. Hay có thể hiểu là việc dự báo hoàn toàn không
đúng so với thực tế.

Nhận xét: việc xây dựng danh mục tối ưu bằng lý thuyết Markowitz chỉ dự báo khá tốt về
độ biến động ( độ lệch chuẩn) đo lường về rủi ro, còn đối với lợi nhuận kỳ vọng hay tỷ số
Sharpe thì khá yếu và không thể sử dụng được.

3.3. Mô hình CAPM


Để sử dụng mô hình CAPM định giá tài sản vốn tính toán lợi nhuận kỳ vọng của danh
mục cổ phiếu, ta giả sử lãi suất của thị trường là 0,5%, ta thực hiện các mã Python sau đây
để lấy hệ số beta của mỗi cổ phiếu trong một năm nhất định, sau đó tính toán lợi nhuận kỳ
vọng của cổ phiếu trong năm tới, dựa trên hệ số beta của nó và hiệu quả hoạt động của danh
mục đầu tư thị trường. Ở đây lựa chọn danh mục thị trường là chỉ số VNIndex trong cùng
khoản thời gian.
Ta thực hiện như sau :
Input [33]: r = 0.005
Input[34]: market = 'VNI'

Giải thích ý nghĩa: Gán giá trị cho r của thị trường là 0,005 và tham chiếu số liệu danh mục
thị trường VNI là đối tượng market

Input [35]: rets = np.log(raw / raw.shift(1)).dropna()


Giải thích ý nghĩa: mục đích của đoạn mã Python này để tính lợi nhuận của danh mục thị
trường market, trong đó sử dụng hàm log của numPy để tính logarit lợi nhuận của các cổ
phiếu trong tệp raw đã khai báo, đồng thời loại bỏ các hàng có giá trị thiếu.

- Tiếp theo ta thực hiện tính toán lợi nhuận dự kiến của các mã cổ phiếu theo mô hình
CAPM. Mô hình CAPM giả định rằng lợi nhuận của một cổ phiếu là một hàm tuyến tính của
lợi nhuận của thị trường. Tỷ lệ của lợi nhuận của cổ phiếu với lợi nhuận của thị trường được
gọi là beta.

Input [36]: res = pd.DataFrame()


for sym in rets.columns[:10]:
print('\n' + sym)
print(54 * '=')
for year in range(2018, 2022):
rets_ = rets.loc[f'{year}-01-01':f'{year}-12-31']
muM = rets_[market].mean() * 52
cov = rets_.cov().loc[sym, market]
var = rets_[market].var()
beta = cov / var
rets_ = rets.loc[f'{year + 1}-01-01':f'{year + 1}-12-31']
muM = rets_[market].mean() * 52
mu_capm = r + beta * (muM - r)
mu_real = rets_[sym].mean() * 52
res = pd.concat([res, pd.DataFrame({'symbol': sym,
'mu_capm': mu_capm,
'mu_real': mu_real},
index=[year + 1])], sort=True)
print('{} | beta: {:.3f} | mu_capm: {:6.3f} | mu_real:
{:6.3f}'
.format(year + 1, beta, mu_capm, mu_real))

Giải thích ý nghĩa: đoạn mã Python này để thực hiện tính toán beta, lợi nhuận dự kiến theo
mô hình CAPM và lợi nhuận thực tế cho mỗi mã cổ phiếu trong mỗi năm. Cụ thể các bước
như sau:
- res = pd.DataFrame(): tạo một đối tượng res là 1 dataFrame trống để lưu trữ
kết quả tính toán.
- for sym in rets.columns[:10]:
print('\n' + sym)
print(54 * '=')
Chạy vòng lặp hiển thị các mã cổ phiếu (đối tượng sym từ 10 cột đầu tiên trong đối
tượng rets). Sau đó in kết quả với tiêu đề cho mỗi cổ phiếu và được ngăn cách bởi
54 dấu =
- for year in range(2018, 2022): đối với mỗi mã cổ phiếu, chạy vòng lặp từ
năm 2018 đến 2021, trong đó:
o rets_ = rets.loc[f'{year}-01-01':f'{year}-12-31'] : lấy dữ liệu từ
đối tượng rets (lợi nhuận) của năm hiện tại, giới hạn từ ngày 01-01 đến
ngày 31-12 của năm. Và lưu kết quả vào đối tượng rets_ để tiện cho việc
thực hiện các tính toán sau.
o muM = rets_[market].mean() * 52: tính toán lợi nhuận (TSSL) trung
bình hàng năm của đối tượng market (danh mục thị trường) có trong rets_
và lưu kết quả vào đối tượng tên là muM; * 52 để chuyển từ dữ liệu ngày
sang dữ liệu theo năm.
o cov = rets_.cov().loc[sym, market]: dùng hàm cov tính toán hiệp
phương sai giữa biến sym (các mã cổ phiếu) và biến market. Là hiệp
phương sai giữa TSSL của các mã cổ phiếu và TSSL của thị trường. lưu kết
quả vào đối tượng tên cov
o var = rets_[market].var(): dùng hàm var tính phương sai của thị
trường và lưu kết quả vào đối tượng tên var
o beta = cov / var: tính beta của cổ phiếu và lưu kết quả vào đối tượng tên
beta
o rets_ = rets.loc[f'{year + 1}-01-01':f'{year + 1}-12-31']: lấy dữ
liệu của đối tượng rets (lợi nhuận) của năm tiếp theo, giới hạn từ ngày
01/01- 31/12 của năm, và lưu vào đối tượng rets_
o muM = rets_[market].mean() * 52 : tính toán tỷ suất sinh lời trung bình
của thị trường trên đối tượng rets_ là tỷ suất sinh lời của năm tiếp theo
o mu_capm = r + beta * (muM - r): tính toán lợi nhuận dự kiến của cổ
phiếu dựa trên mô hình CAPM.
o mu_real = rets_[sym].mean() * 52 : tính toán tỷ suất sinh lời trung bình
hàng năm thực tế của cổ phiếu cho năm tiếp theo.
o res = pd.concat([res, pd.DataFrame({'symbol': sym,
'mu_capm': mu_capm,
'mu_real': mu_real},
index=[year + 1])],
sort=True)
đoạn code này dùng hàm concat của Pandas để thêm hay nói một
DataFrame chứa các đối tượng symbol: mã cổ phiếu; mu_capm: lợi nhuận dự
kiến của cổ phiếu theo mô hình CAPM; và mu_real lợi nhuận thực tế của
cổ phiếu cho năm tiếp theo (với chỉ mục index là year + 1) đã được tính
toán vào DataFrame res.
o print('{} | beta: {:.3f} | mu_capm: {:6.3f} | mu_real: {:6.3f}'
.format(year + 1, beta, mu_capm, mu_real))
Thực hiện in kết quả gồm: năm tiếp theo (year+1), beta, lợi nhuận dự kiến
CAPM (mu_capm) và lợi nhuận thực tế (mu_real) cho mã cổ phiếu. với độ
dài trường dữ liệu là 6 ký tự và lấy sau dấu phẩy 3 số thập phân. Và được
ngăn cách với nhau bằng dấu |

Kết quả hiển thị như sau:

Output [36]: ACB


======================================================
2019 | beta: 1.195 | mu_capm: 0.092 | mu_real: -0.271
2020 | beta: 0.703 | mu_capm: 0.095 | mu_real: 0.209
2021 | beta: 0.914 | mu_capm: 0.280 | mu_real: 0.205
2022 | beta: 1.256 | mu_capm: -0.510 | mu_real: -0.463

BID
======================================================
2019 | beta: 1.591 | mu_capm: 0.121 | mu_real: 0.301
2020 | beta: 1.351 | mu_capm: 0.179 | mu_real: 0.035
2021 | beta: 1.457 | mu_capm: 0.443 | mu_real: -0.255
2022 | beta: 1.351 | mu_capm: -0.549 | mu_real: 0.040

FPT
======================================================
2019 | beta: 1.028 | mu_capm: 0.080 | mu_real: 0.317
2020 | beta: 0.725 | mu_capm: 0.098 | mu_real: 0.025
2021 | beta: 0.762 | mu_capm: 0.234 | mu_real: 0.453
2022 | beta: 0.825 | mu_capm: -0.333 | mu_real: -0.194

HDB
======================================================
2019 | beta: 1.065 | mu_capm: 0.083 | mu_real: -0.118
2020 | beta: 0.330 | mu_capm: 0.047 | mu_real: -0.126
2021 | beta: 1.186 | mu_capm: 0.361 | mu_real: 0.262
2022 | beta: 1.441 | mu_capm: -0.586 | mu_real: -0.673

HPG
======================================================
2019 | beta: 1.067 | mu_capm: 0.083 | mu_real: -0.279
2020 | beta: 1.199 | mu_capm: 0.159 | mu_real: 0.555
2021 | beta: 1.273 | mu_capm: 0.387 | mu_real: 0.113
2022 | beta: 0.804 | mu_capm: -0.325 | mu_real: -0.965

MBB
======================================================
2019 | beta: 1.333 | mu_capm: 0.102 | mu_real: 0.073
2020 | beta: 0.891 | mu_capm: 0.119 | mu_real: 0.094
2021 | beta: 1.110 | mu_capm: 0.339 | mu_real: 0.228
2022 | beta: 1.655 | mu_capm: -0.674 | mu_real: -0.535

PLX
======================================================
2019 | beta: 1.608 | mu_capm: 0.122 | mu_real: 0.063
2020 | beta: 1.216 | mu_capm: 0.161 | mu_real: -0.032
2021 | beta: 1.105 | mu_capm: 0.337 | mu_real: -0.013
2022 | beta: 0.883 | mu_capm: -0.357 | mu_real: -0.541

SAB
======================================================
2019 | beta: 0.359 | mu_capm: 0.031 | mu_real: -0.152
2020 | beta: 0.844 | mu_capm: 0.113 | mu_real: -0.164
2021 | beta: 1.168 | mu_capm: 0.356 | mu_real: -0.256
2022 | beta: 1.095 | mu_capm: -0.444 | mu_real: 0.102

SSI
======================================================
2019 | beta: 1.414 | mu_capm: 0.108 | mu_real: -0.394
2020 | beta: 1.412 | mu_capm: 0.186 | mu_real: 0.592
2021 | beta: 1.258 | mu_capm: 0.383 | mu_real: 0.448
2022 | beta: 1.547 | mu_capm: -0.629 | mu_real: -1.095

VIC
======================================================
2019 | beta: 1.070 | mu_capm: 0.083 | mu_real: 0.193
2020 | beta: 1.183 | mu_capm: 0.157 | mu_real: -0.062
2021 | beta: 0.700 | mu_capm: 0.215 | mu_real: -0.129
2022 | beta: 0.750 | mu_capm: -0.303 | mu_real: -0.581

- Để vẽ đồ thị so sánh lợi nhuận dự báo (dự kiến) của cổ phiếu ACB bởi mô hình
CAPM với hệ số beta từ năm trước và lợi nhuận danh mục thị trường của năm hiện tại, với
lợi nhuận thực tế của cổ phiếu trong năm hiện tại, ta tính toán hiệp phương sai của mã cổ
phiếu ACB giữa lợi nhuận dự kiến theo mô hình CAPM từ beta quá khứ và lợi nhuận thực tế
của cổ phiếu trong năm, như sau:
Đầu tiên ta gán cho đối tượng sym là mã cổ phiếu ACB
Input [37]: sym = 'ACB'
Tiếp theo sử dụng hàm corr tính toán hệ số tương quan (hiệp phương sai) giữa lợi nhuận
dự kiến từ mô hình CAPM và lợi nhuận thực tế của cổ phiếu:
Input [38]: res[res['symbol'] == sym].corr()
Output [38]: mu_capm mu_real
mu_capm 1.000000 0.810636
mu_real 0.810636 1.000000

Từ kết quả thấy được hệ số tương quan của mu_capm và mu_real khá cao 81%, cho thấy
có mối tương quan mạnh giữa 2 biến. Và để quan sát rõ hơn, ta thực hiện biểu diễn bằng
đồ thị với đoạn code Python sau:
Input [39]: res[res['symbol'] == sym].plot(kind='bar',
figsize=(10, 6), title=sym);

Giải thích ý nghĩa: sử dụng hàm plot để vẽ đồ thị dạng thanh cột biểu diễn lợi nhuận dự
kiến theo mô hình CAPM và lợi nhuận thực tế với kích thước của biểu đồ là 10x6 và tiêu đề
là mã cổ phiếu ACB, kết quả hiển thị như sau:
Từ biểu đồ ta thấy được, năm 2019, hai biến mu_capm và mu_real có tương quan
ngược chiều với độ chênh lệch là khá lớn. Các năm 2020, 2021 và 2022, lợi nhuận dự đoán
và thực tế tương quan cùng chiều, năm 2022 tương quan khá mạnh với độ chênh lệch không
đáng kể.
Có thể thấy rằng, CAPM không thực sự hữu ích trong việc dự đoán hiệu suất, lợi nhuận
của cổ phiếu chỉ dựa trên chỉ số beta.
- Xem xét việc sử dụng chỉ số lợi nhuận dự kiến trung bình theo mô hình CAPM có
hiệu quả khi so sánh với lợi nhuận thực tế trung bình. so sánh mức trung bình của lợi nhuận
cổ phiếu dự đoán theo CAPM với mức trung bình của lợi nhuận thực tế. Đầu tiên, thực hiện
tính toán giá trị trung bình của các mã cổ phiếu với đoạn code Python như sau:

Input [40]: grouped = res.groupby('symbol').mean()


grouped

Output[40]: mu_capm mu_real


symbol
ACB -0.010765 -0.079857
BID 0.048332 0.030268
FPT 0.019711 0.150493
HDB -0.023630 -0.163616
HPG 0.076149 -0.144148
MBB -0.028347 -0.034864
PLX 0.065897 -0.130638
SAB 0.014171 -0.117381
SSI 0.012069 -0.112314
VIC 0.038249 -0.144506
Giải thích ý nghĩa: sử dụng hàm groupby trong thư viện Pandas, thực hiện nhóm các giá trị
của biến symbol lấy từ res, sau đó thực hiện tính giá trị trung bình của lợi nhuận dự kiến
theo mô hình CAPM và lợi nhuận thực tế của các mã cổ phiếu; và lưu kết quả vào đối tượng
grouped.
Vẽ đồ thị biểu diễn kết quả tính toán với đoạn code Python như sau (trong đó biểu đồ
thể hiện dưới dạng thanh cột, kích thước là 10x6 và tiêu đề biểu đồ 'Average Values'):
Input [41]: grouped.plot(kind='bar', figsize=(10, 6), title='Average Values');

Từ biểu đồ ta thấy được, sử dụng lợi nhuận trung bình dự đoán theo mô hình CAPM so với
thực tế không có tương quan cùng chiều, và có độ chênh lệch lớn, khác xa so với thực tế ở
đa số các mã cổ phiếu. Điều này cho thấy mô hình CAPM không thực sự hiệu quả chỉ sử
dụng hiệu suất danh mục thị trường và hệ số beta cổ phiếu rõ ràng không thể giải thích được
lợi nhuận quan sát được của các cổ phiếu trên thị trường Việt Nam.

CHƯƠNG 4: KẾT LUẬN

Để xây dựng danh mục đầu tư hiệu quả trên thị trường chứng khoán là việc cần thiết
cho mỗi nhà đầu tư khi tham gia thị trường. Với một thị trường chứng khoán mới nổi, còn
nhiều sự bất cân xứng thông tin, việc sử dụng các lý thuyết theo mô hình Markowitz và mô
hình CAPM góp phần giúp nhà đầu tư có thêm phương pháp, kỹ thuật để xây dựng danh
mục hiệu quả, đa dạng hóa và hạn chế rủi ro cho mình.
Tuy nhiên, mức độ, khẩu vị rủi ro của mỗi nhà đầu tư là khác nhau, do đó, bài tiểu
luận này tuy đã góp phần đưa ra cái nhìn tổng quan để xây dựng danh mục đầu tư, nhưng để
có thể nâng cao hiệu quả dự đoán của mô hình Markowitz cũng như CAPM cần tính toán
đến khẩu vị rủi ro (e ngại, ưa thích hay trung lập với rủi ro) của các nhà đầu tư để đưa ra
được danh mục tối ưu.

You might also like