You are on page 1of 53

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

TRƯỜNG ĐẠI HỌC NGUYỄN TẤT THÀNH


KHOA CÔNG NGHỆ THÔNG TIN

ĐỒ ÁN MÔN HỌC
CHUYÊN ĐỀ CHUYÊN SÂU VỀ KHOA HỌC DỮ LIỆU 1
PHÂN TÍCH THỊ TRƯỜNG CHỨNG KHOÁN

SOFT MOBILE-MUA BÁN

Giảng viên giảng dạy: Hà Minh Tân


Sinh viên thực hiện: Lương Công Thuận
MSSV : 2100011144
Chuyên ngành : KHOA HỌC DỮ LIỆU
Môn học : Chuyên đề chuyên sâu Khoa Học Dữ Liệu
Khóa : 21

Tp.HCM, ngày 18 tháng 1 Năm 2024


BỘ GIÁO DỤC VÀ ĐÀO TẠO
TRƯỜNG ĐẠI HỌC NGUYỄN TẤT THÀNH
KHOA CÔNG NGHỆ THÔNG TIN

ĐỒ ÁN MÔN HỌC
CHUYÊN ĐỀ CHUYÊN SÂU VỀ KHOA HỌC DỮ LIỆU 1
PHÂN TÍCH THỊ TRƯỜNG CHỨNG KHOÁN

SOFT MOBILE-MUA BÁN

Giảng viên giảng dạy: Hà Minh Tân


Sinh viên thực hiện: Đỗ Nguyễn Tùng Dương
MSSV : 2100002821
Chuyên ngành : KHOA HỌC DỮ LIỆU
Môn học : Chuyên đề chuyên sâu Khoa Học Dữ Liệu
Khóa : 21

Tp.HCM, ngày 18 tháng 1 Năm 2024


Trường Đại học Nguyễn Tất Thành CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM
Khoa Công Nghệ Thông Tin Độc lập – Tự do – Hạnh phúc
🙜 🙜 🙝 🙝 🙜 🙜 🙝 🙝

NHIỆM VỤ ĐỒ ÁN
Họ và tên: Lương Công Thuận MSSV: 2100011144
Chuyên ngành: KHOA HỌC DỮ LIỆU Lớp: 21DTH2C
Email: luongcongthuann@gmail.com SĐT : 039418613
Tên đề tài: Phân tích thị trường chứng khoáng và soft mobile-mua bán
Giảng viên giảng dạy: Hà Minh Tân
Thời gian thực hiện: 9/10/2023 đến 18/1/2024
Nhiệm vụ/nội dung (mô tả chi tiết nội dung, yêu cầu, phương pháp… ):

Nội dung và yêu cầu đã được thông qua bộ môn.

TP.HCM, ngày 17 tháng 1 năm 2024


TRƯỞNG BỘ MÔN GIÁO VIÊN HƯỚNG DẪN
(Ký và ghi rõ họ tên) (Ký và ghi rõ họ tên)

LỜI MỞ ĐẦU

Trong thời đại công nghệ ngày nay, tiền điện tử đang ngày càng trở thành một phần quan
trọng của hệ thống tài chính toàn cầu, với Bitcoin nổi bật như một biểu tượng tiêu biểu.
Được sáng tạo bởi Satoshi Nakamoto vào năm 2009, Bitcoin đã trải qua một hành trình
đầy biến động, từ sự chấp nhận ban đầu đến sự quan tâm đối với những biến động giá
không ngừng.

Đồ án này không chỉ là một nỗ lực để hiểu rõ hơn về cơ chế hoạt động của thị trường
Bitcoin mà còn nhằm mục tiêu dự đoán giá của nó trong tương lai. Sự khảo sát sâu rộng
về các yếu tố ảnh hưởng đến giá Bitcoin, từ các yếu tố kỹ thuật như cung và cầu đến
những ảnh hưởng ngoại vi như chính trị và kinh tế, sẽ được thực hiện để xây dựng một
mô hình dự đoán chính xác và đáng tin cậy.

Với lòng tò mò và sự hứng thú với sự phức tạp của thị trường tiền điện tử, chúng tôi bắt
đầu hành trình này với niềm tin rằng nắm bắt được xu hướng giá của Bitcoin không chỉ là
một thách thức mà còn là cơ hội để hiểu sâu hơn về bản chất của tiền tệ số và tác động
của nó đối với thế giới tài chính.
Với sự phát triển ngày càng mạnh mẽ của trí tuệ nhân tạo. Nó đã đem lại những ứng dụng
to lớn trong nhiều lĩnh vực khác nhau như xử lý ngôn ngữ tự nhiên, tự động hoá, thị giác
máy tính, …. Trí tuệ nhân tạo ngày càng trờ thành một phần không thể thiếu của cuộc
sống. Sự tồn tại và phát triển của một doanh nghiệp, cơ quan, tổ chức nhà nước,…Không
thể thiếu sự trợ giúp của trí tuệ nhân tạo. Trong việc thu nhận và xử lý thông tin với khối
lượng ngày càng lớn, nhiều lúc với những việc thủ công không đem lại hiệu quả mong
muốn, lại tốn nhiều công sức và thời gian. Nhằm đem lại sự nhanh chóng và chính xác,
giảm thiểu công sức của con người. Nhóm em đã chọn đề tài “PHÂN TÍCH THỊ
TRƯỜNG CHỨNG KHOÁNG VÀ SOFT MOBILE – MUA BÁN” để nghiên cứu và
viết báo cáo.

LỜI CẢM ƠN

Để thực hiện và hoàn thành bài đề tài này, em đã nhận được sự hỗ trợ, giúp đỡ cũng như
là quan tâm, động viên từ quý Thầy cô trường Đại học Nguyễn Tất Thành, đặc biệt là
khoa Công nghệ thông tin, đã tận tình hướng dẫn, giúp đỡ. Trước hết, em xin bày tỏ tấm
lòng biết ơn sâu sắc với Th.S Hồ Khôi – giảng viên bộ môn “Deep Learning trong khoa
học dữ liệu” người trực tiếp hướng dẫn, cung cấp cho em những kiến thức, kỹ năng cần
thiết để hoàn thành đề tài đã cung cấp một số tài liệu liên quan đến đề tài mà em đang
nghiên cứu và nhắc nhở em đến tiến độ thực hiện đồ án này, mặc dù lịch giảng dạy dày
nhưng luôn dành nhiều thời gian, công sức hướng dẫn em trong suốt quá trình thực hiện
nghiên cứu và hoàn thành đề tài này. Xin gửi lời cảm ơn chân thành đến thầy cô khoa
CNTT, với sự quan tâm, dạy dỗ, chỉ bảo tận tình chu đáo thầy cô đã truyền đạt những
kiến thức quý báu cho em trong suốt thời gian vừa qua. Giúp em hiểu thêm nhiều kiến
thức bổ ích, tinh thần học tập hiệu quả và nghiêm túc, để hoàn thành được đồ án môn học
này. Tuy có nhiều cố gắng trong bài tiểu luận môn học nhưng em còn nhiều hạn chế và bỡ
ngỡ nên không tránh khỏi những thiếu sót. Em rất mong nhận được những ý kiến đóng
góp, giúp đỡ của thầy để bài tiểu luận của em được hoàn thiện hơn đồng thời tạo điều
kiện để em bổ sung, nâng cao ý thức và trình độ chuyên môn của mình trong lĩnh vực
này.

Một lần nữa em xin chân thành cảm ơn!


Sinh viên thực hiện

TRƯỜNG ĐẠI HỌC NGUYỄN TẤT THÀNH KỲ THI KẾT THÚC HỌC PHẦN
TRUNG TÂM KHẢO THÍ HỌC KỲ I NĂM HỌC 2023 - 2024

PHIẾU CHẤM THI TIỂU LUẬN/ĐỒ ÁN


Môn thi: Chuyên Đề Chuyên Sâu Khoa Học Dữ Liệu Lớp học phần: 21DTH2D
Nhóm sinh viên thực hiện :
1. Phan Thanh Phong Tham gia đóng góp: 10%
2. Đỗ Nguyễn Tùng Dương Tham gia đóng góp: 35%
3. Diệp Anh Tuấn Tham gia đóng góp: 10%
4. Ngô Thủy Tiên Tham gia đóng góp: 10%
5. Lương Công Thuận Tham gia đóng góp: 15%
6. Trương Thanh Tín Tham gia đóng góp: 15%
7. Nguyễn Đức Lương Tham gia đóng góp: 5%
Ngày thi: 18/01/2024 Phòng thi:L.613

Đề tài tiểu luận/báo cáo của sinh viên :PHÂN TÍCH VÀ DỰ ĐOÁN GIÁ BITCOIN
Phần đánh giá của giảng viên (căn cứ trên thang rubrics của môn học):
Tiêu chí (theo Điểm tối Điểm đạt
Đánh giá của GV
CĐR HP) đa được
Cấu trúc của Gồm 7 chương
1
báo cáo
Nội dung
• Các nội
dung thành Chương 2, 3, 4, 5, 6 và 7 5
phần
• Lập luận
2
Chương 1
• Kết luận
1
Trình bày
Theo chuẩn format luận văn font chữ 13, canh trái, 1
phải,…
TỔNG ĐIỂM 10
Giảng viên chấm thi
(ký, ghi rõ họ tên)

NHẬN XÉT CỦA GIẢNG VIÊN GIẢNG DẠY

Tp.HCM, Ngày 18 tháng 1 năm 2024


Giảng viên giảng dạy
(Ký tên và ghi rõ họ tên)

DANH MỤC HÌNH


Hình 1 Mạng RNN...........................................................................................................3
Hình 2 Mạng Neu-ron hồi quy.........................................................................................4
Hình 3 Các loại bài toán RNN.........................................................................................4
Hình 4 GRU.....................................................................................................................7
Hình 5 LSTM...................................................................................................................8
Hình 6 Bidirectional (BRNN)..........................................................................................9
Hình 7 Deep (DRNN)....................................................................................................10
Hình 8 Màn hình khi mới khởi chạy..............................................................................26
Hình 9 Sau khi chọn file dữ liệu....................................................................................27
Hình 10 Lấy dữ liệu từ internet thông qua yfinance......................................................27
Hình 11 Sau khi nhấn ‘Show plot in this window (static image)’.................................28
Hình 12 Sau khi nhấn ‘Show plot in browser (interactive)’..........................................28
Hình 13 Tab ‘Predict’.....................................................................................................29
Hình 14 Tùy chỉnh các thông số cho việc huấn luyện...................................................29
Hình 15 Sau khi hoàn thành quy trình dự đoán.............................................................30
Hình 16 Giá trị dự đoán.................................................................................................30
Hình 17 Giá trị thực tế...................................................................................................30
Hình 18 Mô hình MVC trong Flutter.............................................................................32
Hình 19 Kết quả của column và row..............................................................................35
Hình 20 Kết quả của Stack.............................................................................................36
Hình 21 Container chứa Logo........................................................................................37
Hình 22 SizedBox..........................................................................................................38
Hình 23 SingleChildScrollView.....................................................................................39
Hình 24 Bố cục của Widget khi chia với tỉ lệ tương ứng...............................................40
Hình 25 Cấu trúc thư mục..............................................................................................41
Hình 26 Home AppBar..................................................................................................43
Hình 27 Page AppBar....................................................................................................44
Hình 28 BottomNavBar.................................................................................................45
Hình 29 Trang Chủ ( Home)..........................................................................................46
Hình 30 Trang chi tiết sản phẩm....................................................................................49
Hình 31 Sản phẩm trong trang Home............................................................................50
Hình 32 Sản phẩm trong mục yêu thích.........................................................................51
Hình 33 Sản phẩm trong Adbanner................................................................................52
Hình 34 Biểu tượng danh mục sản phẩm.......................................................................52
Hình 35 Thể loại mặt hàng (mục thiết bị điện tử)..........................................................54
Hình 36 Giỏ hàng chứa sản phẩm bàn phím bluetooth..................................................57
Hình 37 Thanh Toán Sản Phẩm......................................................................................58
Hình 38 Đưa sản phẩm vào mục ưa thích......................................................................60
Hình 39 Trạng thái giao hàng.........................................................................................63
Hình 40 Biên lai của sản phẩm......................................................................................63

KÍ HIỆU CÁC CỤM TỪ VIẾT TẮT

Chữ viết tắt Ý nghĩa


RNN Recurrent Neural Network
GRU Gated Recurrent Unit
LSTM Long Short-Term Memory
BRNN Bidirectional Recurrent Neural Network
DRNN Deep Recurrent Neural Network
CNN Convolutional Neural Network
NN Neural Network
GD Gradient Descent
SGD Stochastic Gradient Descent
MSE Mean Squared Error
MAE Mean Absolute Error

CHƯƠNG 1

GIỚI THIỆU

1.1 Giới thiệu đề tài

Dự đoán giá Bitcoin là một đề tài quan trọng trong lĩnh vực tài chính và tiền điện tử, đặc
biệt là trong bối cảnh Bitcoin trở thành một phần quan trọng của hệ thống tài chính toàn
cầu. Giá của Bitcoin biến đổi mạnh mẽ theo thời gian và được ảnh hưởng bởi nhiều yếu
tố khác nhau, bao gồm cầu và cung, sự quan tâm từ phía các nhà đầu tư, tin tức và sự kiện
thị trường, cũng như tình hình kinh tế toàn cầu.

Dự đoán giá Bitcoin là một nhiệm vụ phức tạp đòi hỏi sự hiểu biết về nền kinh tế, thị
trường tài chính, và công nghệ blockchain. Các nhà nghiên cứu và chuyên gia tài chính
đã áp dụng nhiều phương pháp khác nhau để cố gắng dự đoán giá của loại tiền điện tử
này, bao gồm phân tích kỹ thuật, phân tích cơ bản, mô hình hóa toán học, và sử dụng trí
tuệ nhân tạo.

Đề tài này có ý nghĩa lớn trong việc đánh giá rủi ro và cơ hội đầu tư trong lĩnh vực tiền
điện tử. Chúng ta hy vọng rằng việc nghiên cứu về dự đoán giá Bitcoin sẽ đóng góp vào
sự hiểu biết về tiền điện tử và tiền tệ kỹ thuật số giúp người dùng đưa ra quyết định thông
minh trong việc quản lý tài sản và đầu tư trong thị trường này.

1.2 Lý do chọn đề tài

Lý do chọn đề tài "Dự đoán giá Bitcoin" là để hiểu biết thêm về tiền điện tử, cách thức
hoạt động cũng những ảnh hưởng đến tiền điện tử, giúp hiểu rõ thị trường tiền điện tử và
tài chính kỹ thuật số. Nó có thể giúp đối tượng nghiên cứu và người đầu tư đưa ra quyết
định thông minh, tối ưu hóa lợi nhuận và giảm rủi ro trong môi trường đầu tư đầy thách
thức này.

1.3 Mục tiêu đề tài


Mục tiêu của đề tài "Dự đoán giá Bitcoin" là tập trung vào việc nghiên cứu và phát triển
phương pháp dự đoán giá của loại tiền điện tử này. Nghiên cứu sâu về yếu tố ảnh hưởng
đến giá Bitcoin, phát triển mô hình dự đoán sử dụng các phương pháp phân tích kỹ thuật,
mô hình hóa toán học, và trí tuệ nhân tạo để xây dựng các mô hình dự đoán giá Bitcoin.

1.4 Phương pháp đề tài


Áp dụng các phương pháp học máy và trí tuệ nhân tạo như học sâu (deep learning) để xây
dựng mô hình dự đoán dựa trên dữ liệu lịch sử và thông tin thị trường liên quan. Mạng
nơ-ron, LSTM (Long Short-Term Memory), và RNN (RecurrentNeural Network) có thể
được sử dụng trong việc dự đoán giá.

Phương pháp kết hợp các khía cạnh của phân tích kỹ thuật, phân tích cơ bản, mô hình
toán học và trí tuệ nhân tạo có thể giúp tạo ra các dự đoán giá Bitcoin đáng tin cậy và hữu
ích cho người dùng cuối trong việc đưa ra quyết định đầu tư.

1.5 Đối tượng và phạm vi nghiên cứu

Đối tượng nghiên cứu:

Thị trường bitcoin

Phạm vi của đề tài :

Sử dụng dữ liệu lịch sử từ năm 2010-2023 về giá Bitcoin, giao dịch, cầu và cung, và các
yếu tố thị trường liên quan để phát triển mô hình dự đoán.

Sử dụng phân tích kỹ thuật, phân tích cơ bản, mô hình toán học, và trí tuệ nhân tạo để xây
dựng các mô hình dự đoán giá Bitcoin.
CHƯƠNG 2
ỨNG DỤNG VÀ THUẬT TOÁN
2.1Giới thiệu bài toán
RNN (Mạng Nơ-ron Hồi Quy): Sử dụng kiến trúc vòng lặp để duy trì thông tin trạng thái
trước đó. RNN có thể xử lý thông tin dạng chuỗi (sequence/ time-series), có thể mang
thông tin của frame (ảnh) từ state trước tới các state sau, rồi ở state cuối là sự kết hợp của
tất cả các ảnh để dự đoán hành động.
LSTM (Long Short-Term Memory): Cải tiến của RNN, giải quyết vấn đề vanishing
gradient bằng cách sử dụng các cổng (gates) để kiểm soát luồng thông tin.
Dự đoán giá Bitcoin là một thách thức quan trọng trong thị trường tài chính và tiền điện
tử. Bài toán này đặt ra để hứng thú nhà đầu tư, giao dịch viên, và những người quan tâm
đến thị trường tiền điện tử.
2.2 Mô tả thuật toán

2.2.1 Mạng RNN


Kiến trúc mạng RNN truyền thống: Các mạng neural hồi quy, còn được biến đến như là
RNNs, là một lớp của mạng neural cho phép đầu ra được sử dụng như đầu vào trong khi
có các trạng thái ẩn. Thông thường là như sau:

Tại mỗi bước t , giá trị kích hoạt và đầu ra được biểu diễn như sau:

= + + ) và = + )

Với , , , , là các hệ số được chia sẻ tạm thời và là các hàm kích hoạt.
2.2.2 Phân loại bài toán RNN
- One to one: mẫu bài toán cho Neural Network (NN) và Convolutional Neural Network
(CNN), 1 input và 1 output, ví dụ với CNN input là ảnh và output là ảnh được segment.
- One to many: bài toán có 1 input nhưng nhiều output, ví dụ: bài toán caption cho ảnh,
input là 1 ảnh nhưng output là nhiều chữ mô tả cho ảnh đấy, dưới dạng một câu.
- Many to one: bài toán có nhiều input nhưng chỉ có 1 output, ví dụ bài toán phân loại
hành động trong video, input là nhiều ảnh (frame) tách ra từ video, ouptut là hành động
trong video
- Many to many: bài toán có nhiều input và nhiều output, ví dụ bài toán dịch từ tiếng anh
sang tiếng việt, input là 1 câu gồm nhiều chữ: “I love Vietnam” và output cũng là 1 câu
gồm nhiều chữ “Tôi yêu Việt Nam”.

2.2.3 Hàm tối ưu, hàm mất mát


• Hàm tối ưu
Hàm tối ưu(optimization function) là một hàm được sử dụng trong quá trình tối ưu hóa.
Mục tiêu chính của quá trình tối ưu hóa là tìm ra giá trị của các tham số của mô hình sao
cho hàm mất mát đạt giá trị nhỏ nhất (trong trường hợp tối thiểu hóa). Hàm mất mát
thường là một đo lường của sự chênh lệch giữa giá trị mô hình dự đoán và giá trị thực tế
của dữ liệu đào tạo.
Các hàm tối ưu thông dụng:
- Adam:

Trong đó và là các véc-tơ trung bình chuyển động, và là các ước lượng được điều
chỉnh, là tỉ lệ học, và là các hệ số trọng số, là gradient, là một giá trị nhỏ để tránh chia
cho 0.
- Gradient Descent (GD):

Trong đó là vector tham số tại bước thời gian là tỉ lệ học, là đạo hàm của hàm mất mát
theo

- Stochastic Gradient Descent (SGD):

Trong đó là một chỉ số ngẫu nhiên, thường được chọn từ tập dữ liệu đào tạo.
• Hàm mất mát
Các hàm mất mát (loss function) được sử dụng để đo lường sự chênh lệch giữa giá trị dự
đoán của mô hình và giá trị thực tế của dữ liệu. Mục tiêu chính của một mô hình máy học
là tối thiểu hóa giá trị của hàm mất mát, tức là làm cho dự đoán của mô hình càng gần giá
trị thực tế càng tốt.
Các hàm mất mát thường dùng :
- Mean Squared Error (MSE):

Với là giá trị thực tế , là giá trị dự đoán ứng với mẫu thứ trong dữ liệu
- Mean Absolute Error (MAE):

Với là giá trị thực tế , là giá trị dự đoán ứng với mẫu thứ trong dữ liệu
2.2.4 GRU/LSTM
- GRU: Là một kiến trúc mạng nơ-ron hồi quy (RNN) được thiết kế để giải quyết vấn đề
gradient biến mất. Sử dụng các cổng (gates) để kiểm soát dòng thông tin qua thời gian và
giữ cho mô hình có khả năng học các phụ thuộc dài hạn trong dữ liệu tuần tự.

= tanh([ ⋆ , ] + )

= ⋆ +(1-)⋆

- LSTM: Là một mô hình RNN nâng cao, được thiết kế để giải quyết vấn đề gradient biến
mất. Trong mỗi bước thời gian, LSTM sử dụng một ô nhớ để lưu trữ thông tin quan trọng
và sử dụng cổng đầu vào, cổng đầu ra và cổng quên để kiểm soát luồng thông tin qua thời
gian, hiệu quả trong việc xử lý các chuỗi dài và các phụ thuộc dài hạn.

= tanh([ ⋆ , ] + ) = ⋆ + ⋆

= ⋆
2.2.5 Các biến thể RNNs
- Bidirectional Recurrent Neural Network (BRNN) là một kiến trúc mạng nơ-ron hồi quy
(RNN) đặc biệt thiết kế để xử lý dữ liệu theo cả hai hướng: từ trái sang phải (forward) và
từ phải sang trái (backward). Mục đích chính của BRNN là cung cấp thông tin từ cả hai
hướng để cải thiện khả năng hiểu và dự đoán trong các nhiệm vụ liên quan đến chuỗi dữ
liệu. BRNN có khả năng hiểu thông tin từ cả hai hướng, giúp nó nắm bắt được các mối
quan hệ phức tạp trong dữ liệu tuần tự.Trong một số tình huống, thông tin từ quá khứ và
tương lai đều quan trọng để dự đoán một giá trị tại một thời điểm cụ thể. BRNN giúp cải
thiện khả năng dự đoán trong các tình huống như vậy.

=[ ; ]

Trong đó là trạng thái ẩn từ hướng trái sang phải và là trạng thái ẩn từ hướng phải sang
trái.
- Deep RNN mô hình mạng nơ-ron hồi quy (RNN) mà có nhiều tầng (layers) hơn so với
RNN thông thường. Mỗi tầng trong mạng thường tương ứng với một lớp RNN. Ý tưởng
là sử dụng nhiều tầng để học các mức biểu diễn phức tạp và hierarchical từ dữ liệu đầu
vào.

2.3 Xây dựng bộ dữ liệu


Bộ dữ liệu được lấy từ trang Finance với các dữ liệu sau:
Date: Ngày tháng năm
Price: Giá của bitcoin đây là dữ liệu muốn dự đoán.
Open: Giá bitcoin khi mở phiên giao dịch
High: Giá cao nhất
Low: Giá thấp nhất
Vol.: Chênh lệch tỉ giá có thể giúp đo lường mức độ biến động của giá.

CHƯƠNG 3
XÂY DỰNG ỨNG DỤNG NGÔN NGỮ PYTHON
3.1 Xây dựng ứng dụng và giải thích
3.1.1 Lên kế hoạch thiết kế
Mục tiêu: thiết kế ứng dụng dùng thuật toán RNN / LSTM để dự đoán giá cổ phiếu của
ngày tiếp theo
- Thiết kế UI bằng tkinter và customtkinter
- Cho phép chọn file dữ liệu (csv) hoặc nhập mã để lấy dữ liệu từ internet
- Hiển thị biểu đồ tổng quan
- Thiết kế model ( batch size, epoch, optimizer, loss function, layer, unit per layer)
- Hiển thị biểu đồ huấn luyện
-Dự đoán giá cho ngày tiếp theo

3.1.2 Thiết kế từng phần

- Các thư viện cần dùng:

import os
import yfinance as yf
import numpy as np
import pandas as pd
import plotly.graph_objects as plot

import tkinter.ttk
from tkinter import ttk, filedialog, messagebox, Tk, StringVar
import customtkinter as tk
from PIL import Image
from tkcalendar import DateEntry

import keras
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, Dropout, LSTM

- Khởi tạo chương trình:


Để khởi tạo chương trình dùng tkinter ta thực hiện như sau:

# Set appearance
tk.set_appearance_mode('dark')
tk.set_default_color_theme('blue')

# init window frame


window = tk.CTk()

# config window frame


window.minsize(width=1280,height=720)
window.title('Predict MaketPlace')

# cofig window grid


window.columnconfigure(0,weight=1)
window.columnconfigure(1,weight=2)

def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"):
path_1 = os.getcwd() + '\\fig1.jpeg'
path_2 = os.getcwd() + '\\fig2.jpeg'

if os.path.exists(path_1):
os.remove(path_1)
if os.path.exists(path_2):
os.remove(path_2)
window.destroy()

# Closing window
window.protocol("WM_DELETE_WINDOW", on_closing)

window.mainloop()

Khởi tạo một cửa sổ tên là ‘window’ với 1280 x 720 pixel và phần tiêu đề là ‘Predict
MaketPlace’
Hàm on_closing sẽ được thực hiện khi đóng chương trình và trong ứng dụng này nó sẽ
xóa những hình biểu đồ khi thoát chương trình
- Thành phần đưa dữ liệu vào:
+ Bảng dữ liệu
# Data Table for view data
table = ttk.Treeview(frame_data)

# vertical scrollbar
vsb = ttk.Scrollbar(frame_data, orient="vertical", command=table.yview)
vsb.pack(side='right', fill='y')

# Table style
table_style = ttk.Style()
table_style.theme_use('default')

# - for row below heading


table_style.configure("Treeview",
background = '#374a6b',
foreground= 'black',
fieldbackground='#374a6b')

# - for heading
table_style.configure("Treeview.Heading",
background='#162236',
foreground='black',
fieldbackground='#162236')

# Set scrollbar for table


table.configure(yscrollcommand=vsb.set)

table.pack(fill='both',expand=False)

# Config for table not interupt width of grid column


frame_data.pack_propagate(0)
frame_data.grid(row=1,column=1)

Tạo table với scrollbar bằng tkinter Treeview nằm trong frame_data
Sửa lại định dạng và kiểu mẫu của table bằng tkinter Style
Vì phải sửa lại heading (dòng đầu của bảng) và phần dữ liệu hiển thị nên sẽ cần 2 câu
lệnh configure
Định dạng lại cho table để chỉ sử dụng không gian bên trong frame_data mà không ảnh
hưởng đến kích thước cửa sổ
+ Nút và đường dẫn file dữ liệu
# Show data_name choise
input_name = tk.CTkLabel(master=frame_data,height=20,width=200,text='')
input_name.pack()

# command for reading file csv


def file_input():
global df,name
name = filedialog.askopenfilename(title='Select csv
file',filetypes=[('comma-separated values', '*.csv')])
input_name.configure(text=name)

# Try to open file


try:
df = pd.read_csv(name, date_parser = True).dropna()
except Exception as e:
messagebox.showerror("Something wrong ! ",str(e))
show_table()

def show_table():
global df

# Get header
table['column'] = list(df.columns)
table['show'] = 'headings'

# Show header
for col in table['column']:
table.heading(col,text=col)
table.column(col,width=100,anchor=tk.CENTER, stretch=True)

# Show data
df_rows = df.to_numpy().tolist()
for row in df_rows:
table.insert("","end", values=row)

# Editing data
try:
df = replace_comma(df)
except Exception as e:
print(e)
df['Date'] = df['Date'].astype('datetime64[ns]')
df = df.sort_values(by='Date', ascending=True)

# Read csv file button


input_button = tk.CTkButton(master=frame_data,text = 'Open file',
command=lambda :file_input(),
width=100,height=25,
corner_radius=5,
fg_color='#336fd6',hover_color='#0a1f42')
input_button.pack()

Tạo một lable rỗng (chưa có text) dùng để hiện đường dẫn file
Tạo một nút để mở cửa sổ chọn file
Để thực hiện việc chọn file và xử lý dữ liệu ta tạo 2 hàm:
file_input() cho việc lấy dữ liệu từ file và xử lý dữ liệu để đưa dữ liệu về dạng phù hợp
cho chương trình (được lưu dưới biến toàn cục df )
show_table() dùng cho việc chuyển dữ liệu trong biến toàn cục df biểu diễn dưới dạng
bảng
+ Lấy dữ liệu từ internet

def get_stock_data(ticker, start_date, end_date):


stock_data = yf.download(ticker, start=start_date, end=end_date)
stock_data = stock_data[['Open', 'High', 'Low', 'Close', 'Volume']]
stock_data.columns = ['Open', 'High', 'Low', 'Price', 'Vol']
stock_data.reset_index(inplace=True)

return stock_data

# Token
label_name = tk.CTkLabel(master=frame_input,text="ID")
label_name.pack()

input_label_name = tk.CTkTextbox(master=frame_input,height=20)
input_label_name.pack()

# Date
label_cal_start = tk.CTkLabel(master=frame_input,text="Date start")
label_cal_start.pack()
cal_start = DateEntry(frame_input, width=12, year=2019, month=6, day=22,
background='darkblue', foreground='white', borderwidth=2)
cal_start.pack(padx=10, pady=10)

label_cal_end = tk.CTkLabel(master=frame_input,text="Date end")


label_cal_end.pack()
cal_end = DateEntry(frame_input, width=12, year=2022, month=1, day=1,
background='darkblue', foreground='white', borderwidth=2)
cal_end.pack(padx=10, pady=10)
output_resurt = tk.CTkLabel(master=frame_input,text='Watting...')

date_start = cal_start.get_date()
date_end = cal_end.get_date()

def get_date_from_cal():
global date_start,date_end,df
date_start = date_start.strftime("%Y-%m-%d")
date_end = date_end.strftime("%Y-%m-%d")
try:
df = get_stock_data(input_label_name.get(1.0, "end-
1c"),date_start,date_end)
output_resurt.configure(text="Get data success.",
text_color='#21ed28')
except Exception as e:
output_resurt.configure(text = ("Get data failure. " +
str(e)),text_color='#e8132b' )
show_table()

ik = tk.CTkButton(master=frame_input,text="Get Data", command= lambda


:get_date_from_cal())
ik.pack()
output_resurt.pack()

Để lấy dữ liệu từ internet thông qua thư viện yfinance ta cần 3 thành phần bao gồm:
Tên -> input_label_name : Dùng Textbox để nhận dữ liệu
Ngày bắt đầu -> date_start -> cal_start: Dùng DateEntry từ thư viện tkcalendar
Ngày kết thúc -> date_end -> cal_end: Dùng DateEntry từ thư viện tkcalendar
Hai hàm get_stock_data() và get_date_from_cal() dùng để lấy dữ liệu từ
input_label_name, date_start và date_end chuyển dữ liệu date thành dạng phù hợp và bắt
đầu lấy dữ liệu và gán vào df cuối cùng là gọi hàm show_table() để biểu diễn dữ liệu dưới
dạng bảng
+ Xử lý dữ liệu:

def convert_mark(x):
if x.endswith("M"):
x = x.replace("M",' ')
x = float(x) * 1000000
return x
elif x.endswith("K"):
x = x.replace("K",' ')
x = float(x) * 1000
return x
elif x.endswith("B"):
x = x.replace("B",' ')
x = float(x) * 1000000000
return x
else:
return x
def to_list2d(data):
d = []
for i in data:
d.append(i[0])
return d

def replace_comma(df):
vol = []
for i in df["Vol."]:
vol.append(convert_mark(i))
df["Vol."] = vol
df["Price"] = df["Price"].str.replace(',', '').astype(float).fillna(0.0)
df["Open"] = df["Open"].str.replace(',', '').astype(float).fillna(0.0)
df["High"] = df["High"].str.replace(',', '').astype(float).fillna(0.0)
df["Low"] = df["Low"].str.replace(',', '').astype(float).fillna(0.0)

return df

Đôi khi sẽ có những dữ liệu do số quá lớn nên trên các nền tảng dữ liệu họ sẽ chuyển các
con số thành những chữ cái như K để biểu thị cho nghìn, M cho hàng triệu và B cho hàng
tỷ. Ngoài ra họ còn có thể thêm các dấu chấm hoặc phẩy để biểu diễn các con số cho dễ
nhìn hơn.
Nhưng như thế thì mô hình máy học không thể hiểu được và sẽ gặp lỗi khi huấn luyện
vậy nên ta cần chuyển các chữ cái và ký tự đó về dạng phù hợp
Vì cần biểu diễn hai phần khác nhau một phần sẽ biểu diễn biểu đồ phần còn còn lại sẽ là
phần huấn luyện và dự đoán nên ta sẽ chia phần bên dưới thành hai tab: ‘Plot’ và
‘Predict’
- Biểu đồ dữ liệu:

# Tab
tab_display = tk.CTkTabview(master=window)

# Config tab
tab_display.add('Plot')
tab_display.add('Predict')
# Set first tab display
tab_display.set('Plot')

# Inside tab 'Plot'

frame_display = tk.CTkFrame(master=tab_display.tab('Plot'))

# Prepare grid display


frame_display.columnconfigure(0,weight=1)
frame_display.columnconfigure(1,weight=1)

def show_input_plot_interactive(df):
try:
fig = plot.Figure(data=[plot.Candlestick(x=df['Date'],
open=df['Open'],
high=df['High'],
low=df['Low'],
close=df['Price'],
name = 'Candlestick'),

plot.Scatter(x = df['Date'],
y = df['Price'],
name = 'Price',
visible='legendonly',
line=dict(color='#3b24d1'))])
fig.update_layout(xaxis_rangeslider_visible=False,title_text = "")
fig.show()
except Exception as e:
print(str(e))

# Button to show plot


show_plot = tk.CTkButton(master=frame_display, text='Show plot in browser
(interactive)', command=lambda :show_input_plot_interactive(df),
width=100,height=25,
corner_radius=5,
fg_color='#336fd6',
hover_color='#0a1f42')
show_plot.grid(row=0,column=0)

# Set first image to show


img = tk.CTkImage(light_image=Image.open('base.jpeg'),size=(700,500))
img_label = tk.CTkLabel(master=frame_display, text="", image=img)
img_label.configure(width=1280,height=480)
def show_input_plot_img(df):
try:
fig = plot.Figure(data=[plot.Candlestick(x=df['Date'],
open=df['Open'],
high=df['High'],
low=df['Low'],
close=df['Price']
)])
fig.update_layout(xaxis_rangeslider_visible=False)

fig.write_image('fig1.jpeg')
global img
img = tk.CTkImage(light_image=Image.open('fig1.jpeg'),size=(700, 500))
img_label.configure(image=img)
img_label.image = img
except Exception as e:
print(str(e))

show_plot_img = tk.CTkButton(master=frame_display, text='Show plot in this


window (static image)', command=lambda :show_input_plot_img(df),
width=100,height=25,
corner_radius=5,
fg_color='#336fd6',
hover_color='#0a1f42')
show_plot_img.grid(row=0,column=1)

img_label.grid(row=1,column=0,columnspan=2)

frame_display.grid(row=2,sticky='EW',columnspan = 2)

Tạo hai nút với hai cách tạo biểu đồ khác nhau từ thư viện plotly
- Dự đoán :

# Inside tab predict


frame_predict = tk.CTkFrame(master=tab_display.tab('Predict'))
frame_predict.columnconfigure(1,weight=10)
# define variable
optimizer ='adam'
loss = 'mean_squared_error'
batch_size = 20
epochs = 20
layer = 2
unit = 20
fig_state = tk.IntVar()
var_batch = StringVar()
var_batch.set('Batch_size: '+str(batch_size))
var_epoch = StringVar()
var_epoch.set('Epochs: '+ str(epochs))
var_layer = StringVar()
var_layer.set('Number of hiden layer: '+ str(layer))
var_unit = StringVar()
var_unit.set('Number of hiden layer: '+ str(layer))
predict_price = StringVar()
predict_price.set("Predict price : ")

Xác định các biến cần sử dụng và khởi tạo giá trị mặc định
+ Optimizer: Cho phép chọn các optimizer để huấn luyện và dự đoán

# Label Optimizer
# Label
optimizer_name = tk.CTkLabel(master=frame_predict,text='Optimizer')
optimizer_name.grid(column = 0, row = 0)
# function to get choicen optimizer
def get_optimizer(choice):
global optimizer
optimizer = choice
# Option of optimizer
optimizer_menu = tk.CTkOptionMenu(frame_predict,
values=['SGD','RMSprop','Adam',

'AdamW','Adadelta','Adagrad',

'Adamax','Adafactor','Nadam','Ftrl'],
command=get_optimizer)
optimizer_menu.grid(column = 0, row = 1)

+ Loss function: Cho phép chọn loss function cho mô hình

# Label Loss function


loss_name = tk.CTkLabel(master=frame_predict,text='Loss')
loss_name.grid(column = 0, row = 2)
def get_loss(choice):
global loss
loss = choice
# option of loss function
loss_menu = tk.CTkOptionMenu(frame_predict,
values=['mean_squared_error','mean_absolute_error',
'mean_absolute_percentage_error',

'mean_squared_logarithmic_error'],
command=get_loss)
loss_menu.grid(column = 0, row = 3)

+ Batch size : Cho phép tùy chỉnh kích thước mỗi batch

# Batch size label


# Function to get batch size
def get_batch_size(value):
global batch_size
batch_size =int(np.round(value))
var_batch.set('Batch_size '+str(batch_size))
# Label
batch_size_label = tk.CTkLabel(master=frame_predict,textvariable=var_batch)
batch_size_label.grid(column = 0, row = 4)
# Silder
batch_size_slider = tk.CTkSlider(frame_predict, from_=20, to=200,
command=get_batch_size ,width=500)
batch_size_slider.grid(column = 0, row = 5)

+ Epoch : Điều chỉnh số bước lặp quá trình train

# Epochs Label
def get_epochs(value):
global epochs
epochs =int(np.round(value))
var_epoch.set('Epochs: '+str(epochs))

epochs_label = tk.CTkLabel(master=frame_predict,textvariable=var_epoch)
epochs_label.grid(column = 0, row = 6)
epochs_slider = tk.CTkSlider(frame_predict, from_=1, to=100,
command=get_epochs ,width=500)
epochs_slider.grid(column = 0, row = 7)

+ Layer : Có thể thêm số lượng các lớp LSTM tối thiểu là 1 tối đa là 15 ( trong lớp mô
hình đã có sẵn 1 lớp)

# Layer config

def get_layer(value):
global layer
layer =int(np.round(value))
var_layer.set('Number of hiden layer: '+ str(layer))

layer_label = tk.CTkLabel(master=frame_predict,textvariable=var_layer)
layer_label.grid(column = 0, row = 8)
layer_slider = tk.CTkSlider(frame_predict, from_=1, to=15,
command=get_layer ,width=500)
layer_slider.grid(column = 0, row = 9)

+ Unit : Số neuron có mỗi lớp ( lớp mặc định có 50 unit )


# Unit config
def get_unit(value):
global unit
unit =int(np.round(value))
var_unit.set('Number of unit for train: '+ str(unit))

unit_label = tk.CTkLabel(master=frame_predict,textvariable=var_unit)
unit_label.grid(column = 0, row = 10)

unit_textbox = tk.CTkSlider(frame_predict, from_=1, to=200,


command=get_unit ,width=500)
unit_textbox.grid(column = 0, row = 11)

+ Checkbox: Tùy chọn xem biểu đồ huấn luyện mô hình ( Có chọn: Mở xem ở browser,
Không chọn: Xem dưới dạng ảnh)

# Show plot
checkbox_plot = tk.CTkCheckBox(master=frame_predict, variable=fig_state,
onvalue=1,offvalue=0, text="Show interative figure")
checkbox_plot.grid(column = 0, row = 12)

+ Nút bắt đầu huấn luyện: Thực hiện quá trình huấn luyện bằng hàm run_predict()

begin = tk.CTkButton(master=frame_predict,text='Run',command=lambda :
run_predict())
begin.grid(column = 0,row=13)

+ Processbar: Biểu diễn quá trình huấn luyện mô hình

Processbar = tkinter.ttk.Progressbar(master=frame_predict ,orient =


tkinter.HORIZONTAL,length = 300, mode = 'determinate' ,maximum = epochs)
Processbar.grid(column = 0, row=14)

+ Predict_price: Giá trị mô hình dự đoán

Predict_price = tk.CTkLabel(master=frame_predict,textvariable=predict_price)
Predict_price.grid(column =0 , row = 15)

+ img_predict: Vùng hiển thị ảnh quá trình huấn luyện nếu checkbox được chọn

img_pred = tk.CTkImage(light_image=Image.open('base.jpeg'),size=(700,500))
img_predict = tk.CTkLabel(master=frame_predict,image=img_pred, text="Notthing
to show")
img_predict.grid(column = 1, row = 0, rowspan = 26)

+ Hàm run_predict()
def run_predict():
global optimizer, loss, epochs, batch_size, img_predict,
fig_state,layer ,unit
Processbar.configure(maximum = epochs)
Processbar['value'] = 0
cur_run = LSTMclass(df, optimizer, loss, epochs, batch_size, layer,
fig_state.get(),unit)

values = cur_run.run()
predict_price.set("Predict values : " + str(values))

# temp.run(optimizer, loss, epochs, batch_size)


if fig_state.get() == 0:
img = tk.CTkImage(light_image=Image.open('fig2.jpeg'), size=(700,
500))
img_predict.configure(image=img)
img_predict.image = img

Đặt giá trị tối đa của processbar bằng số epoch và đưa giá trị processbar về 0
Khởi tạo LSTMclass bằng các thông số df, optimizer, loss, epochs, batch_size, layer,
fig_state.get(),unit và gán vào cur_run
Vì LSTMclass.run() sẽ trả về giá trị dự đoán nên ta gán giá trị đó vào values và thay đổi
Predict_price bằng giá trị values
Sau đó hiển thị ảnh trên img_predict
3.2 Tiến hành áp dụng thuật toán và chạy chương trình
- Áp dụng thuật toán
Xây dựng lớp LSTMclass

class LSTMclass:
def __init__(self,data,opt,los,epo,batch_si,nlayer,fig_stat,nunit):
self.df = data
self.optimizer = opt
self.loss = los
self.epoch = epo
self.batch = batch_si
self.layer = nlayer
self.unit_p_layer = int(nunit)
self.fig_state = fig_stat
self.test_end = self.df.iloc[0]["Date"]
self.test_start = self.df.iloc[60]["Date"]

Hàm run()
Thêm cột Tomorrow vào df

# Add new column ['Tomorrow']


self.df["Tomorrow"] = self.df["Price"].shift(-1)

Chuẩn hóa dữ liệu bằng MinMaxScaler

# prep data for training


scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(self.df['Price'].values.reshape(-1, 1))

Chuẩn bị dữ liệu cho quá trình huấn luyện

# Set how many data to test-train (will recalculate)


prediction_days = 100

# training data
x_train = [] # Input data
y_train = [] # Target data

# Set which data will be used for training


for x in range(prediction_days, len(scaled_data)):
x_train.append(scaled_data[x - prediction_days:x, 0])
y_train.append(scaled_data[x, 0])

# Finish prepare data


x_train, y_train = np.array(x_train), np.array(y_train)
x_train = np.reshape(x_train, (x_train.shape[0], x_train.shape[1], 1))

Xây dựng mô hình

# build model
model = Sequential()

# Input layer
model.add(LSTM(units=50, return_sequences=True, input_shape=(x_train.shape[1],
1)))
model.add(Dropout(0.2))
model.add(LSTM(units=50, return_sequences=True))
model.add(Dropout(0.2))
# Hidden layer
for i in range(0,self.layer):
model.add(LSTM(units=self.unit_p_layer, return_sequences=True))
model.add(Dropout(0.2))

#output layer
model.add(LSTM(units=20))
model.add(Dropout(0.2))
model.add(Dense(units=1))

Chọn optimizer và lossfunction

# dicided what optimizer and loss function will be use


model.compile(optimizer=self.optimizer, loss=self.loss )

Bắt đầu huấn luyện

# Begin training
model.fit(x_train, y_train, epochs=self.epoch,
batch_size=self.batch ,callbacks=[CustomCallback()])

Chuẩn bị dữ liệu để test

# Prepare test data


test_data = self.df.loc[(self.df['Date'] > self.test_start)]
actual_prices = test_data["Price"]
total_dataset = pd.concat((self.df["Price"], test_data["Price"]), axis=0)

model_inputs = total_dataset[len(total_dataset) - len(test_data) -


prediction_days:].values
model_inputs = model_inputs.reshape(-1, 1)
model_inputs = scaler.transform(model_inputs)

x_test = []

for x in range(prediction_days, len(model_inputs)):


x_test.append(model_inputs[x - prediction_days:x, 0])

x_test = np.array(x_test)
x_test = np.reshape(x_test, (x_test.shape[0], x_test.shape[1], 1))

Thử nghiệm dự đoán


# Testing
prediction_prices = model.predict(x_test)
prediction_prices = scaler.inverse_transform(prediction_prices)

Chuẩn bị dữ liệu cho dự đoán ngày tiếp theo (tiếp sau ngày có trong bộ dữ liệu huấn
luyện)

next_data = [model_inputs[len(model_inputs) - prediction_days:, 0]]


next_data = np.array(next_data)
next_data = np.reshape(next_data, (next_data.shape[0], next_data.shape[1], 1))

Dự đoán ngày tiếp theo


# Predict
prediction = model.predict(next_data)
prediction = scaler.inverse_transform(prediction)

- Chạy thử

Hình 8 Màn hình khi mới khởi chạy

Hình 9 Sau khi chọn file dữ liệu

Hình 10 Lấy dữ liệu từ internet thông qua yfinance

Hình 11 Sau khi nhấn ‘Show plot in this window (static image)’

Hình 12 Sau khi nhấn ‘Show plot in browser (interactive)’

Hình 13 Tab ‘Predict’

Hình 14 Tùy chỉnh các thông số cho việc huấn luyện

Hình 15 Sau khi hoàn thành quy trình dự đoán


Hình 17 Giá trị thực tế

Chương 4
Kết luận

1.Kết quả đạt được


Thông qua việc thực hiện tiểu luận này đã giúp chúng em học hỏi thêm nhiều điều mà
trước đây các bài tập thông thường trên trường lớp vẫn chưa nhắc tới như:
- Biết và sử dụng thêm được nhiều thư viện mới của Python
- Việc tạo một model huấn luyện rất khó nếu không biết cách chính xác và việc này sẽ
dẫn tới việc kết quả dự đoán bị sai lệch quá mức
- Việc chuẩn bị và sàng lọc dữ liệu rất quan trọng, việc loại bỏ hay thay thế các giá trị
không chính xác sẽ ảnh hưởng tích cực tới việc huấn luyện mạng khiến việc huấn luyện
có thể nhanh và chính xác hơn
- Một vài thư viện sẽ phát sinh lỗi dẫn tới việc không thể thi hành chương trình vì thư
viện đó lại cần một thư viện khác với phiên bản nhất định.
- Việc xây dựng quá nhiều lớp hoặc lớp có quá nhiều neuron thì tăng thời gian huấn luyện
và thử nghiệm lên đáng kể
- Đôi khi gặp phải nhiều vấn đề do thiếu hiểu biết sâu về thư viện đang dùng
1. Hạn chế và hướng phát triển
Việc áp dụng mô hình huấn luyện mạng nơ ron vào dự đoán giá cổ phiếu đã khá thành
công. Về cơ bản thị trường rất khó đoán nên việc dự đoán của chương trình cũng chỉ là
dự đoán không hơn không kém tuy nhiên nó đem lại một cái nhìn tổng quát về tương lai
có thể xảy ra nó có thể cho ta một giá trị để ta quyết định nên mua cổ phiếu vào lúc nào
và bán vào lúc nào. Bằng cách áp dụng nhiều thư viện khác nhau để xây dựng nên
chương trình nó giúp chúng em tìm hiểu thêm nhiều kiến thức mới, cách dùng mới, các
hàm chưa dùng tới, và các thư viện mới thuận tiện cho việc thực hiện chương trình.
Ngoài ra việc tìm hiểu ấy cũng dẫn chúng em tìm thấy nhiều điều bất ngờ về thị trường,
tiền tệ, ngân hàng, ... Chương trình đã đưa ra được kết quả dự đoán là một kết quả đáng
mừng vì đã hoàn thành được mục tiêu đề ra.
Chúng em không lưu lại model hay trọng số cho mỗi lần huấn luyện là do thị trường mỗi
cổ phiếu không giống nhau, chúng điều có những điểm khác biệt về sự thay đổi tuỳ theo
các sự kiện, ngày,… nếu dùng model của một cổ phiếu này mà dùng để dự đoán cổ phiếu
khác là không chính xác được, nó tương tự như việc dùng dữ liệu nhận dạng các loại mèo
để dự đoán một con cá vậy. Tuy nhiên, sau này có thể nhóm em sẽ thêm vào tính năng
đấy sau.
Trong tương lai chắc chắn sẽ có nhiều phương pháp khác để dự đoán giá cả cổ phiếu hơn
do công nghệ sẽ ngày càng phát triển nhiều ý tưởng sẽ được nảy sinh. Nhưng cuối cùng
giá cổ phiếu tiếp theo vẫn sẽ do con người thao túng, việc dự đoán mãi sẽ chỉ là dự đoán
vẫn mãi chỉ là dự đoán, chỉ là một cái nhìn khác từ máy tính cho con người tham khảo.

Chương 5
MVC TRONG FLUTTER VÀ NHỮNG WIDGET CƠ BẢN
5.1 MVC trong Flutter

Hình MVC trong Flutter, một framework phát triển ứng dụng di động đa nền tảng. Mô
hình này chia ứng dụng Flutter thành ba thành phần chính:
5.1.1 Model
-Lớp Model: Chứa dữ liệu và logic xử lý dữ liệu của ứng dụng.
Ví dụ: Lớp UserModel lưu trữ thông tin về người dùng, bao gồm tên, email, mật khẩu,
v.v.

5.1.2 View
-Widget: Các thành phần giao diện người dùng (UI) được xây dựng bằng các widget của
Flutter.
Ví dụ: Widget Text hiển thị văn bản, widget TextField cho phép người dùng nhập liệu,
v.v.
5.1.3 Controller
-Lớp State: Quản lý trạng thái của widget và tương tác với Model để lấy dữ liệu.
Ví dụ: Lớp UserState quản lý trạng thái của widget UserView, bao gồm dữ liệu người
dùng hiện tại, trạng thái đăng nhập, v.v.
5.1.4 Mối quan hệ của các thành phần
-Model: Cung cấp dữ liệu cho View thông qua Controller.
-View: Hiển thị dữ liệu từ Model và nhận tương tác từ người dùng.
-Controller: Xử lý tương tác từ người dùng và lấy dữ liệu từ Model để cập nhật View.
Lợi ích của mô hình MVC trong Flutter:
-Dễ dàng bảo trì: Việc tách biệt logic xử lý dữ liệu khỏi giao diện người dùng giúp cho
việc bảo trì ứng dụng dễ dàng hơn.
-Khả năng mở rộng cao: Mô hình MVC cho phép dễ dàng thêm các tính năng mới cho
ứng dụng mà không ảnh hưởng đến các phần khác của ứng dụng.
-Tái sử dụng code: Các widget có thể được tái sử dụng trong các ứng dụng khác.
Ví dụ về mô hình MVC trong Flutter:
Ứng dụng Todo List:
-Model: Lớp Todo lưu trữ thông tin về một việc cần làm.
-View: Widget TodoList hiển thị danh sách các việc cần làm.
-Controller: Lớp TodoController quản lý trạng thái của widget TodoList, bao gồm danh
sách các việc cần làm, thêm việc mới, v.v.
5.2 Những Widget cơ bản trong Flutter
Flutter hiện đang là framework moblie app đang được quan tâm trong thời gian gần đây
bởi nó vừa có thể giảm thời gian phát triển và chi phí ( thứ mà native framework không
có được ) hay đảm bảo performance ( một điểm yếu của react native). Flutter framework
giống như một tập hợp của rất nhiều widget, và widget giống như một loại vật liệu để xây
dựng lên app của bạn. Khi bạn xây dựng một màn hình hiển thị trên app bằng Flutter
cũng giống như bạn đang chơi một trò chơi xếp hình, công việc của bạn là phải tìm được
widget phù hợp để lắp ghép vào chỗ trống cho hoàn hảo nhất
5.2.1 Text
Đây là widget cơ bản giúp bạn format text trong ứng dụng của bạn, và thuộc tính quan
trọng nhất.
Text(
'Hello World',
style: TextStyle(
color: Colors.black,
fontSize: 40,
backgroundColor: Colors.white,
fontWeight: FontWeight.bold,
)
)

5.2.2 Column, Row


Đây là 2 widget giúp định hình 1 tập hợp các widget theo chiều dọc (vertically) hoặc
chiều ngang (horizontally). 2 thuộc tính quan trọng nhất của 2 widget này là
mainAxisAlignment và crossAxisAlignment.
Khi bạn sử dụng một Row, các widget con của nó được sắp xếp thành một hàng, theo
chiều ngang. Vì vậy, trục chính của Row là nằm ngang. Sử dụng mainAxisAlignment
trong Row cho phép bạn căn chỉnh các widget con của hàng theo chiều ngang (ví dụ: left,
right). Trục chéo đến trục chính của Row là trục dọc. Vì vậy, sử dụng
crossAxisAlignment trong Row cho phép bạn xác định cách con của nó được căn chỉnh
theo chiều dọc. Trong một Cột thì ngược lại. Con của một Column được sắp xếp theo
chiều dọc, từ trên xuống dưới (theo mặc định).

columnn(
MainAxisAlignment : MainAxisAlignment.start,
Children : [
SizedBox
height: 40,
),
Text(
'Hello World!',
Style : TextStyle(
fontSize: 30,
fontWeight: Fontweight.bold,
color: Colors.blacks,

),
),
SizedBox(
height: 40,
),
Text(
'This is Sample App',
style: TextStyle(
fontSize: 30,
colo,r : Colors.grey,
)
),
],
),
5.2.3 Stack
Cũng giống như Column hay Row, Stack cũng là 1 tập hợp các widget, tuy nhiên điểm
khác biệt là các widget con trong stack có thể chồng lên nhau. Thường đi kèm với widget
này là widget Positioned để căn chỉnh vị trí của từng widget trong Stack, hãy theo dõi ví
dụ dưới để hiểu hơn về stack:

5.2.4 Container

Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Color.amber,
border: border.all(width: 1, Color: colors.black26),
borderRadius: borderRadius.circular(10)
),
padding: EdgeInsets.all(50)
child:FlutterLogo(
Size: 50,
),
),

5.2.5 SizedBox
Đây là widget mình hay sử dụng để tạo khoảng cách giữa các widget hơn là việc layout
một view:

columnn(
MainAxisAlignment : MainAxisAlignment.start,
Children : [
SizedBox
height: 20,
),
Text(
'Hello World!',
Style : TextStyle(
fontSize: 30,
fontWeight: Fontweight.bold,
color: Colors.blacks,

),
),
SizedBox(
height: 20,
),
Text(
'This is Sample App',
style: TextStyle(
fontSize: 20,
colo,r : Colors.grey,
)
),
],
),
5.2.6 SingleChildScrollView
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: column(
children: [
LogoBox(),
LogoBox(),
LogoBox(),
LogoBox(),
LogoBox(),
LogoBox(),
]
),
),

class LogoBox extends StatelessWidget(


const LogoBox({
key key ,
}) : super(key: key):
@Override
Widget BiquadFilterNode(BuildContext context){
return CSSContainerRule(
width : 200,
height: 200,
margin: edgeInsets.all(20),
decoration: BoxDecoration(
color: VideoColorSpace.amber,
border: ReadableStreamBYOBReader.call(width:1,color:
colors.black26),
borderRadius: BorderRadius.circular(10),
),
padding: EdgeInsets.all(50),
child: FlutterLogo(
Size: 50,
),
):
}.
),

5.2.7 Expanded
Đây là widget hay được sử dụng để chia bố cục của widget cha thành các phân với tỉ lệ
tương ứng:
Column(
children:[
Expanded(
flex: 2,
child: container(color: Colors.green),
),
Expanded(
flex: 3,
child:container(color: Colors.blue),
),
Expanded(
child: Container(color: Color.grey),
),
],
),

Chương 6 Xây Dựng SOFTPHONE – MUA BÁN


6.1 Giới thiệu dự án
- Dự án "Ứng dụng Bán Hàng SOFTPHONE " nhằm xây dựng một ứng dụng di động đa
nền tảng sử dụng framework Flutter của Google. Ứng dụng này cho phép người dùng
xem và mua các sản phẩm, phân loại hàng, quản lý giỏ hàng, thêm sản phẩm ưa thích và
xem các hóa đơn, ..

assets
• icon: Lưu trữ biểu tượng SVG sử dụng trong ứng dụng.
• images: Lưu trữ hình ảnh được sử dụng trong ứng dụng.

lib
• main.dart: File gốc, chứa widget chính như AppBar, BottomAppBar.
• data.dart: Quản lý dữ liệu sản phẩm và các hàm xử lý dữ liệu.
• constants.dart: Lưu trữ các hằng số thường được sử dụng.
screen
- cart: Lưu trữ trang giỏ hàng.
- category: Lưu trữ trang danh mục sản phẩm.
- favorite: Lưu trữ trang danh mục yêu thích.
- home:
+ home_screen.dart: Trang chính hiển thị sản phẩm.
+ list_item.dart: Cấu trúc widget chung được sử dụng cho các trang.
- product:
+ product.dart: Lưu cấu trúc dữ liệu sản phẩm và các cấu trúc dữ liệu khác.
+ product_main.dart: Trang chính của chi tiết sản phẩm.
- receipt:
+ receipt_list.dart: Trang chính hiển thị danh sách hóa đơn.
+ receipt_model.dart: Các widget cho trang hóa đơn.
pubspec.yaml : File cấu hình cho ứng dụng, bao gồm các dependencies và các tùy chọn
khác.
6.2 Các Trang Và Chức Năng Chính
6.2.1 Trang main.dart định hình MyAppBar_Home, MyAppBar_Page và
BottomNavBar cho App
- Thanh MyAppBar_Home
main.dart:
<Wigget> MyAppBar_Home
AppBar
|-- leading: IconButton
| |-- icon: SvgPicture.asset
|-- title: Container
| |-- child: TextField
| |-- decoration: InputDecoration
|-- actions:
|-- IconButton
| |-- icon: SvgPicture.asset
|-- IconButton
|-- onPressed: () { Navigator.pushNamed(context, '/cart'); }
|-- icon: SvgPicture.asset
• Nút quay về trang trước (leading): IconButton này có biểu tượng mũi tên quay
về. Khi nhấn, nó sử dụng Navigator.pop để quay về trang trước đó, giống như việc
quay lại từ chi tiết sản phẩm hoặc từ trang tìm kiếm.
• Ô tìm kiếm sản phẩm (title): Container này chứa một ô tìm kiếm để người dùng
có thể nhập từ khóa để tìm kiếm sản phẩm trong cửa hàng.
• Nút hành động (actions):
• Nút thông báo: IconButton chứa biểu tượng thông báo
• Nút giỏ hàng: IconButton này chứa biểu tượng giỏ hàng. Khi nhấn, nó sử dụng
Navigator.pushNamed để chuyển đến trang giỏ hàng ('/cart').

- MyAppBar_Page
<Wigget> MyAppBar_Page
AppBar
|-- leading: IconButton
| |-- icon: SvgPicture.asset
| |-- onPressed: () { Navigator.pop(context); }
|-- title: Container
| |-- child: TextField
| |-- decoration: InputDecoration
|-- actions:
|-- IconButton
| |-- icon: SvgPicture.asset
|-- IconButton
|-- onPressed: () { Navigator.pushNamed(context, '/cart'); }
|-- icon: SvgPicture.asset
• Nút quay về trang trước (leading): IconButton này có biểu tượng mũi tên quay
về. Khi nhấn, nó sử dụng Navigator.pop để quay về trang trước đó, giống như việc
quay lại từ chi tiết sản phẩm hoặc từ trang tìm kiếm.
• Ô tìm kiếm sản phẩm (title): Container này chứa một ô tìm kiếm để người dùng
có thể nhập từ khóa để tìm kiếm sản phẩm trong cửa hàng.
• Nút hành động (actions):
- Nút thông báo hoặc hoạt động tùy chọn khác: IconButton này có thể chứa biểu
tượng thông báo hoặc các tùy chọn khác, tùy thuộc vào yêu cầu của ứng dụng bán
hàng.
- Nút giỏ hàng: IconButton này chứa biểu tượng giỏ hàng. Khi nhấn, nó sử dụng
Navigator.pushNamed để chuyển đến trang giỏ hàng ('/cart').

Hình 27 Page AppBar

- BottomNavBar
<Wigget> MyBottomNavBar
final int currentIndex;
final ValueChanged<int> onTap;
BottomNavigationBar
|-- currentIndex: currentIndex
|-- selectedItemColor: Colors.deepOrangeAccent
|-- unselectedItemColor: Colors.black
|-- onTap: onTap
|-- items:
|-- BottomNavigationBarItem 1
| |-- icon: SvgPicture.asset("assets/icon/house-chimney.svg", color: Colors.black)
| |-- label: "Home"
|-- BottomNavigationBarItem 2
| |-- icon: SvgPicture.asset("assets/icon/receipt.svg", color: Colors.black)
| |-- label: "Receipt"
|-- BottomNavigationBarItem 3
|-- icon: SvgPicture.asset("assets/icon/heart.svg", color: Colors.black)
|-- label: "Favorites"
• currentIndex: Tham số này xác định mục nào sẽ được chọn khi thanh điều hướng
được vẽ lần đầu tiên. Nó thường được giữ bởi một biến được quản lý để theo dõi
trạng thái hiện tại của ứng dụng.
• selectedItemColor: Màu sắc của mục hiện đang được chọn (active).
• unselectedItemColor: Màu sắc của các mục không được chọn (inactive).
• onTap: Hàm xử lý sự kiện được gọi khi một mục được nhấn. Thông thường, bạn
sẽ cập nhật currentIndex dựa trên mục được chọn.
• items: Danh sách các mục trong thanh điều hướng. Mỗi mục được đại diện bởi
một BottomNavigationBarItem. Mỗi mục bao gồm:
• icon: Biểu tượng của mục, ở đây là một hình vector (SVG).
• label: Nhãn hiển thị bên cạnh biểu tượng, ví dụ: "Home", "Receipt",
"Favorites".

Hình 28 BottomNavBar

6.2.2 Trang Chính (home_screen.dart)


<file> Home_screen.dart:
return Scaffold
|-- appBar: MyAppBar_Home
|-- body: Column
| |-- Center
| | |-- CarouselSlider
| | |-- adbanner_slider items
| |-- Container
| | |-- Row
| | |-- Expanded
| | |-- Align
| | |-- ListView.builder (category)
| |-- Expanded
| |-- ListView.builder (dataCard)
| |-- ProductContainer items
|-- bottomNavigationBar: MyBottomNavBar

Hình 29 Trang Chủ ( Home)

6.2.3 Trang Chi Tiết Sản Phẩm


Cấu trúc:
Scaffold
|-- appBar: MyAppBar_Page()
|-- body: Column
|-- children
|-- Expanded
|-- child: ListView
|-- children
|-- Padding
|-- child: CarouselSlider
|-- items: item.image.map
|-- Builder
|-- Container
|-- child: Image.asset(path, fit: BoxFit.cover)
|-- Container
|-- child: Text
|-- item.name
|-- Text
|-- (item.price.toString() + " VND")
|-- Container
|-- child: Row
|-- children
|-- Text
|-- item.ratting.toString()
|-- InkWell
|-- child: SvgPicture.asset('assets/icon/star.svg')
|-- Spacer
|-- flex: 1
|-- InkWell
|-- onTap: ()
|-- Provider.of<data>(context, listen:
false).addToFavorite(item);
|-- child: Container
|-- child: Text
|-- 'Add to favorite'
|-- Container
|-- child: Text
|-- item.detail
|-- bottomNavigationBar: Container
|-- child: Row
|-- children
|-- Expanded
|-- flex: 7
|-- child: Container
|-- child: Row
|-- children
|-- Expanded
|-- flex: 1
|-- child: GestureDetector
|-- onTap: ()
|-- if (quantity > 1)
|-- setState
|-- quantity == quantity--;
|-- child: Container
|-- child: Center
|-- child:
svgPicture.asset('assets/icon/minus.svg')
|-- Expanded
|-- flex: 1
|-- child: Container
|-- child: Text
|-- quantity.toString()
|-- Expanded
|-- flex: 1
|-- child: GestureDetector
|-- onTap: ()
|-- setState
|-- quantity == quantity++;
|-- child: Container
|-- height: 50
|-- color: Colors.deepOrange
|-- child: Center
|-- child: SvgPicture.asset('assets/icon/plus.svg')
|-- Expanded
|-- flex: 4
|-- child: GestureDetector
|-- onTap: ()
Provider.of<data>(context, listen:
false).addItemToCart(item.id, quantity);
|-- child: Container
|-- child: Center
|-- child: Row
|-- children
|-- Text
|-- 'Add to cart'
|-- SvgPicture.asset('assets/icon/cart-minus.svg',
height: 20 ,width: 20, color: Colors.white,)

Hình 30 Trang chi tiết sản phẩm

<file> List_item.dart:
<Wigget> ProductContainer
final product_card product;
final void Function()? onTap;
return GestureDetector
|-- onTap: onTap
|-- Container
|-- padding: EdgeInsets.symmetric(vertical: 10)
|-- decoration: BoxDecoration
|-- color: Colors.grey[100]
|-- border: Border (bottom)
|-- BorderSide
|-- color: Colors.grey
|-- width: 1.0
|-- Row
|-- Image.asset (product.imageUrl, height: 80)
|-- Column
|-- Container (product title)
|-- Text (product.title)
|-- Row (product rating)
|-- Text (product.rating)
|-- SvgPicture.asset ('assets/icon/star.svg', width: 12)
|-- Text ('\${product.price}')

Hình 31 Sản phẩm trong trang Home


<Wigget> Favorate_product
final product_card product;
final void Function()? onTap;
return GestureDetector
|-- onTap: onTap
|-- Container
|-- padding: EdgeInsets.symmetric(vertical: 10)
|-- decoration: BoxDecoration
| |-- color: Colors.grey[100]
| |-- border: Border (bottom)
| |-- BorderSide
| |-- color: Colors.grey
| |-- width: 1.0
|-- Row
|-- Image.asset (product.imageUrl, height: 80)
|-- Column
| |-- Container (product title)
| |-- Text (product.title)
| |-- Row (product rating)
| |-- Text (product.rating)
| |-- SvgPicture.asset ('assets/icon/star.svg', width: 12)
|-- GestureDetector (Remove from favorites)
|-- onTap: ()
|-- Container
|-- height: 35
|-- width: 35
|-- SvgPicture.asset ('assets/icon/circle-xmark.svg', width: 20,
height: 20)

Hình 32 Sản phẩm trong mục yêu thích

<Wigget> Adbanner_content:
final Adbanner_data addata;
return GestureDetector
|-- onTap: ()
|--Navigator.push
|-- MaterialPageRoute
|--builder: (context) => product_item(item:
data().get_product_by_id(addata.id))
|-- Container
|-- margin: EdgeInsets.only(top: 10)
|-- height: 100
|-- width: 400
|-- child: Image.asset(addata.imageurl)
Hình 33 Sản phẩm trong Adbanner
<Wigget> category_wigget:
final category_base category;
return GestureDetector
|-- onTap: ()
|--Navigator.push
|-- MaterialPageRoute( builder: (context) =>
|--category_main (category: data().getCategory(category.id),)
|-- Container
|-- padding: EdgeInsets.symmetric(vertical: 10)
|-- height: 50
|-- child: Column
|-- SvgPicture.asset (category.icon, height: 20, width: 20)
|-- Spacer (flex: 5)

- Thanh AppBar:
• App bar hiển thị tiêu đề và icon của trang chính.
• Phần thân của trang sử dụng Consumer để lắng nghe và cập nhật dữ liệu từ data
provider.
• Sử dụng ListView.builder để hiển thị danh sách sản phẩm từ productList trong data
provider.
• Mỗi sản phẩm được hiển thị bằng ProductItemCard.
- Danh Sách Sản Phẩm:
• Sử dụng HomeScreen để hiển thị danh sách sản phẩm.
• Sử dụng ProductItemCard để tạo card cho mỗi sản phẩm trong danh sách.
• Sử dụng Consumer để lắng nghe thay đổi trong dữ liệu và cập nhật giao diện.
- Thanh Điều Hướng (BottomNavigationBar):
• Hiển thị thanh điều hướng dưới cùng với các tùy chọn Home, Receipt và Favorite.
• Sử dụng MyBottomNavBar để tạo thanh điều hướng.

6.2.4 Danh Mục


Truy cập trang danh mục sản phẩm để xem các loại sản phẩm khác nhau.
<file> category_main.dart:
return Scaffold
|-- appBar: MyAppBar_Page()
|-- body: Consumer<data>
| |-- builder: (context, value, child)
| |-- Column
| |-- Expanded
| |-- ListView.builder
| |-- scrollDirection: Axis.vertical
| |-- itemCount: category_item.length
| |-- itemBuilder
| |-- ProductContainer
| |-- product: category_item[index]
| |-- onTap: ()
| |-- Navigator.push => product_item(item:
data().get_product_by_id(category_item[index].id))
|-- bottomNavigationBar: MyBottomNavBar
- Scaffold Widget:
• App bar hiển thị tiêu đề và icon của trang danh mục.
• Phần thân của trang sử dụng Consumer để lắng nghe và cập nhật dữ liệu từ data
provider.
• Sử dụng ListView.builder để hiển thị danh sách sản phẩm thuộc danh mục từ
category_item trong category_main.

Hình 35 Thể loại mặt hàng (mục thiết bị điện tử)

- ProductContainer Widget:
• Dùng để hiển thị thông tin của từng sản phẩm trong danh mục.
• Sử dụng GestureDetector để xử lý sự kiện khi người dùng chọn một sản phẩm.
• Khi sản phẩm được chọn, chuyển đến trang chi tiết sản phẩm (product_item.dart).

6.2.5 Giỏ Hàng


Cấu trúc:
Scaffold
|-- appBar: AppBar
|-- leading: backbutton()
|-- title: Container
|-- child: Row
|-- children
|-- Text
|-- 'Cart'
|-- SvgPicture.asset
|-- 'assets/icon/cart-minus.svg',
|-- backgroundColor: Colors.deepOrange
|-- body: Consumer<data>
|-- builder: (context, value, child)
|-- Column
|-- children
|-- Expanded
|-- child: ListView.builder
|-- scrollDirection: Axis.vertical
|-- itemCount: value.cart_item_list.length
|-- itemBuilder: (context, index)
|-- return item_cart
|-- item: value.cart_item_list[index]
|-- index_at: index
|-- cart_data: value
|-- bottomNavigationBar: Consumer<data>
|-- builder: (context, value, child)
|-- Container
|-- child: Row
|-- children
|-- Expanded
|-- flex: 2
|-- child: Container
|-- child: Column
|-- children
|-- Text
|-- "Tổng số tiền: "
|-- Text
|-- "${value.getCartPrice()} VND"
|-- Expanded
|-- flex: 1
|-- child: GestureDetector
|-- child: Container
|-- child: Center
|-- Text
|-- 'Thanh Toán'
|-- onTap: ()
|-- showModalBottomSheet
|-- useRootNavigator: true
|-- context: context
|-- builder: (BuildContext context)
|-- Container
|-- child: Center
|-- Column
|-- children
|-- Text
|-- 'Price: ${value.getCartPrice()}'
|-- Container
|-- child: Row
|-- children
|-- Container
|-- Row
|-- children
|-- SvgPicture.asset
|-- 'assets/icon/credit-card.svg',
|-- Padding
|-- Text
|-- "Master card: 1234***"
|-- Container
|-- SvgPicture.asset
|-- 'assets/icon/angle-small-right.svg',
|-- Container
|-- child: SlideAction
|-- text: 'Slide to pay'
|-- submittedIcon: SvgPicture.asset
|-- 'assets/icon/check.svg',
|-- sliderButtonIcon: SvgPicture.asset
|-- 'assets/icon/angle-double-right.svg',
|-- sliderRotate: false
|-- onSubmit: ()
|-- value.addItemtoReciept()
|-- value.clearCartItem()
|-- Navigator.pop(context)
Có thể thêm, xóa, ... sản phẩm vào giỏ hàng và xem giỏ hàng.
- Hiển Thị Giỏ Hàng:
• Sử dụng ListView.builder để hiển thị danh sách các sản phẩm trong giỏ hàng.
• Mỗi sản phẩm trong giỏ hàng được hiển thị thông qua widget item_cart.
• Mỗi item_cart bao gồm thông tin sản phẩm, số lượng, giá và các tùy chọn như cập
nhật số lượng, xóa sản phẩm khỏi giỏ hàng.
- Tính Toán Tổng Tiền:
• Hiển thị tổng số tiền của các sản phẩm trong giỏ hàng dưới dạng thanh điều hướng
dưới cùng.
• Sử dụng value.getCartPrice() để lấy tổng giá trị giỏ hàng.
- Chức Năng Thanh Toán:
• Hiển thị nút "Thanh Toán" dưới dạng nút ElevatedButton.
• Khi người dùng nhấn vào "Thanh Toán", một modal bottom sheet sẽ xuất hiện để
xác nhận thanh toán.
• Sử dụng SlideAction để thực hiện hành động "Slide to pay" và cập nhật trạng thái
khi thanh toán thành công.
- Chức Năng Quản Lý Số Lượng Sản Phẩm:
• Cho phép người dùng cập nhật số lượng sản phẩm trong giỏ hàng.
• Sử dụng các biểu tượng "+" và "-" để tăng và giảm số lượng.
- Chức Năng Xóa Sản Phẩm:
• Cho phép người dùng xóa sản phẩm khỏi giỏ hàng.
• Sử dụng biểu tượng "trash" để thực hiện hành động xóa.

Hình 36 Giỏ hàng chứa sản phẩm bàn phím bluetooth

Hình 37 Thanh Toán Sản Phẩm

6.2.6 Chi Tiết Sản Phẩm


Cung cấp một trải nghiệm chi tiết với thông tin sản phẩm, đánh giá, mô tả và các chức
năng quan trọng như thêm vào yêu thích và giỏ hàng.
- Hiển Thị Thông Tin Sản Phẩm:
• Hiển thị hình ảnh của sản phẩm trong một CarouselSlider cho phép người dùng
xem nhiều hình ảnh.
• Hiển thị tên sản phẩm, giá, đánh giá và mô tả sản phẩm.
- Đánh Giá Sản Phẩm:
• Hiển thị số điểm đánh giá và cung cấp biểu tượng sao để người dùng có thể đánh
giá sản phẩm.
• Khi người dùng nhấn vào một biểu tượng sao, có thể xử lý sự kiện đánh giá.
- Chức Năng Thêm vào Yêu Thích:
• Người dùng có thể thêm sản phẩm vào danh sách Yêu Thích.
• Sử dụng một nút hoặc văn bản để thực hiện thao tác này.
- Mô Tả Chi Tiết Sản Phẩm:
• Hiển thị mô tả chi tiết về sản phẩm, cho phép người dùng đọc thông tin chi tiết về
sản phẩm.
- Chức Năng Thêm vào Giỏ Hàng:
• Cho phép người dùng thêm sản phẩm vào giỏ hàng với số lượng được quản lý
thông qua các biểu tượng "+" và "-".
• Một nút "Thêm vào giỏ hàng" có sẵn để thực hiện thêm vào giỏ hàng.
- Quản Lý Số Lượng Thêm vào Giỏ Hàng:
• Cho phép người dùng quản lý số lượng sản phẩm cần thêm vào giỏ hàng thông
qua các biểu tượng "+" và "-"
6.2.7 Trang sản phẩn ưa thích
Cung cấp một trải nghiệm người dùng thân thiện bằng cách hiển thị danh sách sản phẩm
yêu thích
<file> favorite_main.dart
return Scaffold
|-- appBar: MyAppBar_Home()
|-- body: Consumer<data>
| |-- builder: (context, value, child)
| |-- Column
| |-- Expanded
| |-- ListView.builder
| |-- scrollDirection: Axis.vertical
| |-- itemCount: value.getFavoriteCard().length
| |-- itemBuilder
| |-- Favorate_product
| |-- product: value.getFavoriteCard()[index]
| |-- onTap: ()
| |-- Navigator.push => product_item(item:
value.favorite_list[index])
|-- bottomNavigationBar: MyBottomNavBar
- Danh Sách Sản Phẩm Yêu Thích:
• Hiển thị danh sách sản phẩm yêu thích dưới dạng danh sách cuộn.
• Mỗi sản phẩm yêu thích được đại diện bởi một Favorite_product

Hình 38 Đưa sản phẩm vào mục ưa thích

6.2.8 Hóa Đơn


Cấu trúc:
Cấu trúc trang:
Scaffold
|-- appBar: MyAppBar_Home()
|-- body: Consumer<data>
|-- builder: (context, value, child)
|-- Column
|-- children
|-- Expanded
|-- child: ListView.builder
|-- scrollDirection: Axis.vertical
|-- itemCount: value.reciept_list.length
|-- itemBuilder: (context, index)
|-- return receipt_card
|-- data_card: value.reciept_list[index]
|-- index: index
|-- bottomNavigationBar: MyBottomNavBar
Cấu trúc receipt_card:
Consumer<data>
|-- builder: (context, value, child)
|-- GestureDetector
|-- onTap: ()
|-- Navigator.push
|-- context
|-- MaterialPageRoute
|-- builder: (context) => product_item(item:
value.get_product_by_id(data_card.id))
|-- child: Container
|-- child: Column
|-- children
|-- Container
|-- child: Row
|-- children
|-- Container
|-- child: Row
|-- children
|-- Image.asset
|-- value.get_product_by_id(data_card.id).image[0],
|-- Column
|-- children
|-- Container
|-- child: Text
|-- data_card.name
|-- Container
|-- child: Row
|-- children
|-- Text
|-- 'Số lượng: ${data_card.quantity}'
|-- Text
|-- 'Tổng giá trị: ${data_card.price}'
|-- Container
|-- child: SvgPicture.asset
|-- 'assets/icon/angle-small-right.svg'
|-- Container
|-- child: data_card.complete_status
|-- const Text
|-- 'Giao Hàng Thành Công !'
|-- Text
|-- data_card.status
|-- data_card.complete_status
|-- Container
|-- Container
|-- child: Row
|-- children
|-- Container
|-- child: ElevatedButton
|-- onPressed: ()
|-- Text
|-- 'Yêu cầu hoàn tiền'
|-- Container
|-- child: ElevatedButton
|-- onPressed: ()
|-- Provider.of<data>(context, listen:
false).setCompleteStatus(index);
|-- Text
|-- 'Đã Nhận được hàng'

Trang Hóa Đơn cung cấp quản lý danh sách hóa đơn và chi tiết hóa đơn
- Danh Sách Hóa Đơn:
• Mỗi hóa đơn được hiển thị thông qua widget receipt_card, chứa các thông tin như
hình ảnh sản phẩm, tên, số lượng, giá trị, trạng thái, và nút chuyển hướng đến
trang chi tiết sản phẩm khi nhấn vào.
- Chức Năng Yêu Cầu Hoàn Tiền và Nhận Hàng:
• Cho phép người dùng yêu cầu hoàn tiền và xác nhận đã nhận hàng.
• Nếu hóa đơn đã hoàn thành, hiển thị thông báo "Giao Hàng Thành Công !" hoặc
trạng thái hiện tại của hóa đơn.
- Chức Năng Yêu Cầu Hoàn Tiền và Đã Nhận Hàng (ElevatedButton):
• Nếu hóa đơn chưa hoàn thành, hiển thị hai nút "Yêu cầu hoàn tiền" và "Đã Nhận
được hàng" để người dùng thực hiện các hành động.

Hình 39 Trạng thái giao hàng

Hình 40 Biên lai của sản phẩm

Chương 7
Tổng kết và hướng phát triển

Dữ liệu về ứng dụng Android là một phần quan trọng của cả hệ sinh thái di động, đóng vai
trò quyết định trong trải nghiệm người dùng và xu hướng công nghệ hiện đại. Theo dõi và
phân tích dữ liệu này mang lại cái nhìn sâu sắc về sự phát triển và ảnh hưởng của ứng
dụng trên nền tảng Android.Thị trường ứng dụng Android đã chứng kiến sự tăng trưởng
đáng kể, với hàng triệu ứng dụng được phát triển để đáp ứng nhu cầu ngày càng đa dạng
của người dùng. Dữ liệu về lượt tải về, đánh giá, và sử dụng ứng dụng là những chỉ số
quan trọng, cho thấy sự ưa chuộng và độ phổ biến của từng ứng dụng.

Khám phá xu hướng trong dữ liệu ứng dụng cũng là chìa khóa để hiểu rõ hơn về sở thích
và nhu cầu của người dùng. Sự thay đổi trong ưu tiên của họ, từ ứng dụng giáo dục đến
giải trí và công việc, đều được phản ánh qua dữ liệu sử dụng ứng dụng.Tính an toàn và
bảo mật của ứng dụng Android cũng là một điểm quan trọng cần xem xét. Dữ liệu về các
lỗ hổng bảo mật, cập nhật phần mềm, và phản ứng của người dùng đối với các vấn đề an
ninh là yếu tố quyết định đến sự tin cậy và sự thành công của một ứng dụng.

Tóm lại, dữ liệu ứng dụng Android không chỉ là tập hợp các con số, mà là nguồn thông tin
quý giá về sự phát triển của nền tảng di động này. Việc hiểu rõ về xu hướng và thách thức
từ dữ liệu này không chỉ hỗ trợ nhà phát triển ứng dụng mà còn giúp định hình hình ảnh
toàn cầu của thế giới di động.

TÀI LIỆU THAM KHẢO

[1] Nguyễn Thanh Tuấn (2009), Deeplearning cơ bản The Legrand Orange Book
Template by Mathias Legrand is used : https://nttuan8.com/sach-deep-learning-co-ban/
[2] Ths.Hồ Khôi (2022) ,Lecturer in deep learning at the Faculty of Information
Technology, Nguyen Tat Thanh University.
[3] Wikipedia, Long short – term memory (29-11-2023),
https://en.wikipedia.org/wiki/Long_short-term_memory
[4] Wikipedia, Recurrent neural network (27-11-2023) ,
https://en.wikipedia.org/wiki/Recurrent_neural_network
[5] Giảng viên Hà Minh Tân (2023), Lecturer in deep learning at the Faculty of
Information Technology, Nguyen Tat Thanh University. (28 – 12 – 2023)
[6] Flutter documentation, https://docs.flutter.dev/ (10 – 1 – 2024)
[7] Github: Phân tích thị trường chứng khoáng: https://github.com/FuryGox/Predict-
marketplace
[8] Github: Soft- mobile (shoppe_f): https://github.com/FuryGox/shoppe_f
[9] Google Drive:
https://drive.google.com/drive/folders/1QoKMZkrPhikPAmqnD_vpbiJsJ5ImfrNC?
usp=sharing

You might also like