Skip to main content

27 posts tagged with "Python"

Posts related to Python programming

View All Tags

Phỏng vấn học viên: Anh Đào Trung Hiếu – Chuyển nghề thành công với Power BI & Python

· 3 min read

🎥 Phỏng vấn học viên: Anh Đào Trung Hiếu sau khóa học Power BI

🔗 Xem video phỏng vấn trên YouTube



👋 Giới thiệu nhân vật

🎤 Người phỏng vấn: Nguyễn Thế Vinh
👨‍💼 Học viên: Anh Đào Trung Hiếu
📍 Hiện sống tại: Phần Lan
🧑‍🎓 Học viên của: Trung tâm Hướng Nghiệp Dữ Liệu
📘 Các khóa học đã tham gia:

  • Lập trình tự động hóa với Python
  • Lập trình mobile app với Flutter
  • Phân tích và trực quan dữ liệu với Power BI

❓ Điều gì khiến anh chọn học tại Trung tâm?

"Trước đây, tôi làm kế toán hơn 20 năm. Tuy nhiên, nếu không cập nhật công nghệ mới thì sẽ bị tụt hậu. Power BI và Python không còn là công cụ của dân IT, mà trở thành kỹ năng bắt buộc với bất kỳ ai làm văn phòng."

Anh Hiếu từng sử dụng Power BI Desktop từ năm 2017 tại Tân Hiệp Phát, nhưng nhận thấy sự phát triển nhanh chóng của công nghệ. Do đó, anh quyết định học lại để nắm chắc hơn về:

  • Python: xử lý và phân tích dữ liệu (backend)
  • Power BI: trực quan hóa và trình bày dữ liệu (frontend)

⭐ Điều anh Hiếu thích nhất ở khóa học?

  1. Giáo trình thực tế, chuyên sâu:

    • Không dừng ở lý thuyết, tập trung vào bài tập thực tế như xử lý file Excel, PDF, Word, kết nối cơ sở dữ liệu, làm báo cáo bán hàng...
  2. Ứng dụng đa dạng:

    • Python: dùng OCR để đọc hóa đơn PDF, nhập liệu tự động.
    • Power BI: tạo dashboard tương thích cả mobilelaptop.
  3. Chất lượng giảng viên:

    • Thầy Tuấn (Python): hướng dẫn lập trình thực chiến và đóng gói phần mềm.
    • Cô Nguyên (Power BI): dạy cách xây dựng ma trận chỉ tiêu và phân tích hệ thống báo cáo chuyên sâu.

🧪 Anh Hiếu đã áp dụng gì vào công việc?

🔧 Dự án Python:

  • Viết phần mềm bán hàng cho công ty GSC (Long An).
  • Tính năng:
    • Nhận diện hóa đơn PDF (OCR) và nhập liệu tự động.
    • Upload dữ liệu JSON lên SQL Server.
    • Giao diện nhập liệu thân thiện và dễ chỉnh sửa.

📊 Dự án Power BI:

  • Dashboard bán hàng cho công ty sản xuất sản phẩm golf.
  • Hơn 500 sản phẩm, kết nối trực tiếp từ hệ thống.
  • Dashboard hiển thị tốt trên cả webmobile.
  • Tích hợp mô hình Machine Learning để dự báo doanh số.

💬 Kết luận

Anh Hiếu là minh chứng sống động cho sự chuyển đổi nghề nghiệp thành công từ kế toán sang phân tích dữ liệu. Kiến thức từ các khóa học không chỉ giúp anh bắt kịp thời đại mà còn tạo ra giá trị thực tế cho doanh nghiệp.

"Nếu bạn đang làm văn phòng, hãy học Power BI & Python ngay từ hôm nay."


📌 Khám phá thêm các khóa học tại Hướng Nghiệp Dữ Liệu:
👉 https://huongnghiepdulieu.com

10 Tips để Viết Code Python Hiệu Quả

· 4 min read

Python là một ngôn ngữ lập trình mạnh mẽ và dễ học, nhưng để viết code không chỉ chạy đúng mà còn hiệu quả (nhanh và ít tốn tài nguyên), bạn cần lưu ý một số mẹo. Dưới đây là 10 tips giúp bạn cải thiện hiệu suất code Python của mình.

1. Sử dụng Cấu trúc dữ liệu phù hợp

Việc chọn đúng cấu trúc dữ liệu (list, tuple, set, dictionary) có ảnh hưởng lớn đến hiệu suất, đặc biệt là khi làm việc với lượng dữ liệu lớn. Hiểu rõ thời gian thực hiện (time complexity) của các thao tác trên từng cấu trúc dữ liệu là rất quan trọng.

Hiệu Suất Cấu Trúc Dữ liệu Python

2. Tránh Vòng lặp không cần thiết

Python cung cấp nhiều cách để xử lý dữ liệu mà không cần vòng lặp tường minh (ví dụ: list comprehensions, generator expressions, built-in functions như map, filter, reduce). Sử dụng chúng thường hiệu quả hơn và code cũng ngắn gọn hơn.

List Comprehensions vs For Loop

3. Tận dụng Built-in Functions và Libraries chuẩn

Các hàm và thư viện tích hợp sẵn của Python thường được tối ưu hóa cao (thường được viết bằng C). Thay vì tự triển khai lại các chức năng cơ bản, hãy ưu tiên sử dụng những gì Python đã cung cấp.

4. Hiểu về Global và Local Variables

Truy cập biến cục bộ (local variables) thường nhanh hơn biến toàn cục (global variables). Hạn chế sử dụng biến toàn cục trong các vòng lặp nóng (hot loops) hoặc các phần code cần hiệu suất cao.

5. Sử dụng join() cho việc nối chuỗi

Khi nối nhiều chuỗi, đặc biệt là trong vòng lặp, sử dụng phương thức str.join() hiệu quả hơn nhiều so với toán tử +.

String Concatenation: join() vs +

6. Sử dụng Generators khi làm việc với dữ liệu lớn

Generators tạo ra các giá trị một cách lười biếng (lazily), tức là chỉ tạo giá trị khi cần. Điều này giúp tiết kiệm bộ nhớ đáng kể khi làm việc với các tập dữ liệu hoặc chuỗi giá trị lớn.

7. Profile Code của bạn

Đừng đoán về hiệu suất! Sử dụng các công cụ profiling như cProfile hoặc timeit để đo lường chính xác thời gian thực hiện của các phần code khác nhau. Điều này giúp bạn tập trung tối ưu hóa đúng chỗ.

8. Sử dụng NumPy và Pandas cho Tính toán số và Dữ liệu

Đối với các tác vụ liên quan đến mảng, ma trận hoặc phân tích dữ liệu, NumPy và Pandas là các thư viện cực kỳ hiệu quả (cũng được tối ưu hóa bằng C) so với việc xử lý bằng Python thuần.

9. Tránh Import Modules không cần thiết

Import modules tốn thời gian. Chỉ import những gì bạn thực sự cần và tránh import trong các vòng lặp hoặc hàm được gọi thường xuyên.

10. Cân nhắc JIT Compilers (Ví dụ: Numba, PyPy)

Đối với các tác vụ tính toán chuyên sâu, Just-In-Time (JIT) compilers như Numba có thể biên dịch các phần code Python (đặc biệt là các vòng lặp số) sang mã máy hiệu quả hơn. PyPy là một trình biên dịch JIT thay thế cho CPython, có thể tăng tốc nhiều loại code Python.

Kết luận

Việc viết code Python hiệu quả không chỉ giúp ứng dụng của bạn chạy nhanh hơn mà còn sử dụng tài nguyên hiệu quả hơn. Áp dụng 10 tips trên sẽ là bước khởi đầu tốt để bạn viết code Python chất lượng cao hơn.

Tài Liệu Tham Khảo

Các Chiến Lược Giao Dịch Tiền Điện Tử Phổ Biến Sử Dụng Python

· 10 min read
admin

Các Chiến Lược Giao Dịch Tiền Điện Tử Phổ Biến Sử Dụng Python

Chiến lược giao dịch tiền điện tử

Giới thiệu

Giao dịch tiền điện tử đã trở thành một lĩnh vực đầu tư phổ biến với nhiều người tham gia thị trường. Việc áp dụng các chiến lược giao dịch tự động hóa giúp nhà đầu tư loại bỏ cảm xúc khỏi quyết định giao dịch và tận dụng cơ hội thị trường 24/7. Python, với các thư viện phân tích dữ liệu phong phú, đã trở thành ngôn ngữ lập trình ưa thích cho việc triển khai các chiến lược giao dịch tiền điện tử.

Trong bài viết này, chúng tôi sẽ khám phá một số chiến lược giao dịch tiền điện tử phổ biến và cách triển khai chúng bằng Python.

1. Chiến Lược Trung Bình Động (Moving Average Strategy)

Trung bình động là một chỉ báo kỹ thuật phổ biến được sử dụng để xác định xu hướng thị trường. Một chiến lược giao dịch đơn giản là mua khi đường trung bình động ngắn hạn (ví dụ: MA 20) cắt lên trên đường trung bình động dài hạn (ví dụ: MA 50) và bán khi đường ngắn hạn cắt xuống dưới đường dài hạn.

import numpy as np
import pandas as pd
from binance.client import Client

# Khởi tạo client
client = Client(api_key, api_secret)

# Lấy dữ liệu
klines = client.get_historical_klines("BTCUSDT", Client.KLINE_INTERVAL_1DAY, "1 year ago UTC")

# Chuyển đổi sang DataFrame
df = pd.DataFrame(klines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume', 'close_time',
'quote_asset_volume', 'number_of_trades', 'taker_buy_base_asset_volume',
'taker_buy_quote_asset_volume', 'ignore'])

# Xử lý dữ liệu
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df['close'] = pd.to_numeric(df['close'])
df.set_index('timestamp', inplace=True)

# Tính toán trung bình động
df['MA20'] = df['close'].rolling(window=20).mean()
df['MA50'] = df['close'].rolling(window=50).mean()

# Tạo tín hiệu giao dịch
df['signal'] = 0
df['signal'] = np.where(df['MA20'] > df['MA50'], 1, 0)
df['position'] = df['signal'].diff()

# Hiển thị các điểm mua và bán
buy_signals = df[df['position'] == 1]
sell_signals = df[df['position'] == -1]

print("Điểm mua:")
print(buy_signals[['close', 'MA20', 'MA50']])
print("\nĐiểm bán:")
print(sell_signals[['close', 'MA20', 'MA50']])

2. Chiến Lược Momentum

Các chỉ báo Momentum như RSI (Relative Strength Index) đo lường tốc độ thay đổi giá. Một chiến lược thông thường là mua khi thị trường quá bán (RSI < 30) và bán khi thị trường quá mua (RSI > 70).

import pandas as pd
import numpy as np
from binance.client import Client
import talib

# Khởi tạo client
client = Client(api_key, api_secret)

# Lấy dữ liệu
klines = client.get_historical_klines("BTCUSDT", Client.KLINE_INTERVAL_4HOUR, "3 months ago UTC")

# Chuyển đổi sang DataFrame
df = pd.DataFrame(klines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume', 'close_time',
'quote_asset_volume', 'number_of_trades', 'taker_buy_base_asset_volume',
'taker_buy_quote_asset_volume', 'ignore'])

# Xử lý dữ liệu
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df['close'] = pd.to_numeric(df['close'])
df.set_index('timestamp', inplace=True)

# Tính toán RSI
df['RSI'] = talib.RSI(df['close'].values, timeperiod=14)

# Tạo tín hiệu giao dịch
df['signal'] = 0
df['signal'] = np.where(df['RSI'] < 30, 1, 0) # Mua khi RSI < 30
df['signal'] = np.where(df['RSI'] > 70, -1, df['signal']) # Bán khi RSI > 70

# Lọc tín hiệu để tránh nhiều tín hiệu liên tiếp
df['position'] = df['signal'].diff().fillna(0)

# Hiển thị kết quả
buy_signals = df[df['position'] == 1]
sell_signals = df[df['position'] == -1]

print("Tín hiệu mua (RSI quá bán):")
print(buy_signals[['close', 'RSI']])
print("\nTín hiệu bán (RSI quá mua):")
print(sell_signals[['close', 'RSI']])

3. Chiến Lược Grid Trading

Grid Trading là một chiến lược mua và bán tự động ở các mức giá định sẵn trong một phạm vi. Chiến lược này hiệu quả trong thị trường đi ngang (sideway market).

import numpy as np
import pandas as pd
from binance.client import Client
from binance.enums import *

# Khởi tạo client
client = Client(api_key, api_secret)

# Cấu hình grid trading
symbol = "BTCUSDT"
lower_price = 40000 # Giá dưới của grid
upper_price = 50000 # Giá trên của grid
grid_number = 10 # Số lượng grid
investment = 10000 # Tổng số tiền đầu tư (USDT)

# Tính toán khoảng cách giữa các grid
grid_size = (upper_price - lower_price) / grid_number
grid_investment = investment / grid_number

# Tạo các grid
grids = []
for i in range(grid_number + 1):
price = lower_price + i * grid_size
grids.append({
"price": price,
"buy_order": None,
"sell_order": None
})

# Hàm mô phỏng tạo lệnh mua
def place_buy_order(symbol, price, quantity):
# Trong thực tế, bạn sẽ sử dụng client.create_order() để tạo lệnh thực tế
print(f"Đặt lệnh MUA {quantity:.5f} {symbol} tại giá {price:.2f}")
return {"symbol": symbol, "side": "BUY", "price": price, "quantity": quantity}

# Hàm mô phỏng tạo lệnh bán
def place_sell_order(symbol, price, quantity):
# Trong thực tế, bạn sẽ sử dụng client.create_order() để tạo lệnh thực tế
print(f"Đặt lệnh BÁN {quantity:.5f} {symbol} tại giá {price:.2f}")
return {"symbol": symbol, "side": "SELL", "price": price, "quantity": quantity}

# Thiết lập grid bằng cách đặt các lệnh mua tại các mức giá
for i in range(grid_number):
price = grids[i]["price"]
quantity = grid_investment / price
grids[i]["buy_order"] = place_buy_order(symbol, price, quantity)

# Nếu lệnh mua được khớp, đặt lệnh bán ở mức giá cao hơn
if i < grid_number:
sell_price = grids[i+1]["price"]
grids[i]["sell_order"] = place_sell_order(symbol, sell_price, quantity)

print("\nThiết lập Grid Trading hoàn tất!")
print(f"Phạm vi giao dịch: {lower_price:.2f} - {upper_price:.2f} USDT")
print(f"Kích thước grid: {grid_size:.2f} USDT")
print(f"Đầu tư mỗi grid: {grid_investment:.2f} USDT")

4. Chiến Lược DCA (Dollar-Cost Averaging)

DCA là chiến lược đầu tư theo đó bạn đều đặn mua một lượng tiền điện tử cố định trong những khoảng thời gian đều đặn, bất kể giá là bao nhiêu.

import time
import pandas as pd
from binance.client import Client
from datetime import datetime, timedelta

# Khởi tạo client
client = Client(api_key, api_secret)

# Cấu hình DCA
symbol = "BTCUSDT"
investment_amount = 100 # USD mỗi lần đầu tư
interval_days = 7 # Đầu tư mỗi 7 ngày
total_periods = 10 # Tổng số lần đầu tư

# Lấy thông tin giá hiện tại
def get_current_price(symbol):
ticker = client.get_symbol_ticker(symbol=symbol)
return float(ticker['price'])

# Mô phỏng chiến lược DCA
def simulate_dca():
total_investment = 0
total_coins = 0
dca_results = []

# Ngày bắt đầu (giả định từ 70 ngày trước)
start_date = datetime.now() - timedelta(days=70)

for i in range(total_periods):
# Ngày đầu tư
investment_date = start_date + timedelta(days=i * interval_days)

# Lấy dữ liệu giá từ ngày đầu tư
klines = client.get_historical_klines(
symbol,
Client.KLINE_INTERVAL_1DAY,
investment_date.strftime("%d %b, %Y 00:00:00"),
(investment_date + timedelta(days=1)).strftime("%d %b, %Y 00:00:00")
)

if not klines:
print(f"Không có dữ liệu cho ngày {investment_date.strftime('%Y-%m-%d')}")
continue

# Lấy giá đóng cửa
price = float(klines[0][4])

# Tính lượng tiền điện tử mua được
coins_bought = investment_amount / price

# Cập nhật tổng
total_investment += investment_amount
total_coins += coins_bought

# Ghi nhận kết quả
dca_results.append({
"date": investment_date.strftime("%Y-%m-%d"),
"price": price,
"investment": investment_amount,
"coins_bought": coins_bought,
"total_investment": total_investment,
"total_coins": total_coins,
"current_value": total_coins * price,
"roi": (total_coins * price / total_investment - 1) * 100
})

# Chuyển kết quả thành DataFrame
return pd.DataFrame(dca_results)

# Thực hiện mô phỏng
results = simulate_dca()
print(results[["date", "price", "coins_bought", "total_coins", "roi"]])

# Tính toán ROI cuối cùng
current_price = get_current_price(symbol)
final_value = results.iloc[-1]["total_coins"] * current_price
final_roi = (final_value / results.iloc[-1]["total_investment"] - 1) * 100

print(f"\nKết quả cuối cùng tại giá hiện tại ({current_price:.2f} USD):")
print(f"Tổng đầu tư: {results.iloc[-1]['total_investment']:.2f} USD")
print(f"Tổng số coin: {results.iloc[-1]['total_coins']:.8f}")
print(f"Giá trị hiện tại: {final_value:.2f} USD")
print(f"ROI: {final_roi:.2f}%")

5. Chiến Lược Rebalancing

Chiến lược Rebalancing duy trì một tỷ lệ cố định giữa các tài sản khác nhau trong danh mục đầu tư, thực hiện mua và bán định kỳ để đưa các tỷ lệ về mức mục tiêu.

import pandas as pd
import numpy as np
from binance.client import Client
from datetime import datetime, timedelta

# Khởi tạo client
client = Client(api_key, api_secret)

# Cấu hình danh mục đầu tư
portfolio = {
"BTC": 0.5, # 50% Bitcoin
"ETH": 0.3, # 30% Ethereum
"BNB": 0.2 # 20% Binance Coin
}

initial_investment = 10000 # USD
rebalance_frequency = 30 # Rebalance mỗi 30 ngày

# Lấy giá hiện tại
def get_current_prices(symbols):
prices = {}
for symbol in symbols:
ticker = client.get_symbol_ticker(symbol=symbol+"USDT")
prices[symbol] = float(ticker['price'])
return prices

# Mô phỏng chiến lược rebalancing
def simulate_rebalancing():
# Giả định bắt đầu từ 180 ngày trước
start_date = datetime.now() - timedelta(days=180)
current_date = start_date
end_date = datetime.now()

# Dữ liệu ban đầu
current_prices = {}
for symbol in portfolio:
klines = client.get_historical_klines(
symbol+"USDT",
Client.KLINE_INTERVAL_1DAY,
start_date.strftime("%d %b, %Y 00:00:00"),
(start_date + timedelta(days=1)).strftime("%d %b, %Y 00:00:00")
)
if klines:
current_prices[symbol] = float(klines[0][4])

# Tính toán số lượng coin ban đầu
holdings = {}
for symbol, allocation in portfolio.items():
investment_amount = initial_investment * allocation
holdings[symbol] = investment_amount / current_prices[symbol]

rebalance_results = []

# Ghi nhận trạng thái ban đầu
initial_holdings_value = sum(holdings[s] * current_prices[s] for s in portfolio)
rebalance_results.append({
"date": start_date.strftime("%Y-%m-%d"),
"action": "Initial",
"portfolio_value": initial_holdings_value,
"holdings": holdings.copy(),
"prices": current_prices.copy()
})

# Mô phỏng qua thời gian
while current_date < end_date:
current_date += timedelta(days=rebalance_frequency)

# Lấy giá mới
for symbol in portfolio:
klines = client.get_historical_klines(
symbol+"USDT",
Client.KLINE_INTERVAL_1DAY,
current_date.strftime("%d %b, %Y 00:00:00"),
(current_date + timedelta(days=1)).strftime("%d %b, %Y 00:00:00")
)
if klines:
current_prices[symbol] = float(klines[0][4])

# Tính giá trị danh mục hiện tại
current_value = sum(holdings[s] * current_prices[s] for s in portfolio)

# Tính toán cân bằng lại
new_holdings = {}
for symbol, target_allocation in portfolio.items():
# Giá trị mục tiêu
target_value = current_value * target_allocation
# Số lượng coin mới
new_holdings[symbol] = target_value / current_prices[symbol]

# Ghi nhận kết quả rebalance
rebalance_results.append({
"date": current_date.strftime("%Y-%m-%d"),
"action": "Rebalance",
"portfolio_value": current_value,
"holdings": new_holdings.copy(),
"prices": current_prices.copy()
})

# Cập nhật holdings
holdings = new_holdings

# Chuyển kết quả thành DataFrame
return pd.DataFrame(rebalance_results)

# Thực hiện mô phỏng
results = simulate_rebalancing()

# Hiển thị kết quả
for i, row in results.iterrows():
print(f"\n--- {row['date']} ({row['action']}) ---")
print(f"Giá trị danh mục: ${row['portfolio_value']:.2f}")

for symbol in portfolio:
coin_value = row['holdings'][symbol] * row['prices'][symbol]
allocation = coin_value / row['portfolio_value'] * 100
print(f"{symbol}: {row['holdings'][symbol]:.6f} (${coin_value:.2f}, {allocation:.2f}%)")

# Tính ROI
initial_value = results.iloc[0]["portfolio_value"]
final_value = results.iloc[-1]["portfolio_value"]
roi = (final_value / initial_value - 1) * 100

print(f"\nKết quả cuối cùng:")
print(f"Giá trị ban đầu: ${initial_value:.2f}")
print(f"Giá trị cuối cùng: ${final_value:.2f}")
print(f"ROI: {roi:.2f}%")

Kết Luận

Các chiến lược giao dịch tiền điện tử tự động hóa với Python mang lại nhiều lợi thế như loại bỏ cảm xúc từ quá trình giao dịch, tận dụng cơ hội thị trường 24/7, và thực hiện kiểm tra lại (backtesting) để cải thiện hiệu suất. Tuy nhiên, cần lưu ý rằng không có chiến lược nào đảm bảo lợi nhuận và thị trường tiền điện tử có thể rất biến động.

Trước khi triển khai bất kỳ chiến lược giao dịch tự động nào, hãy:

  1. Kiểm tra lại chiến lược trên dữ liệu lịch sử
  2. Bắt đầu với số vốn nhỏ để kiểm tra hiệu quả thực tế
  3. Liên tục theo dõi và điều chỉnh chiến lược khi cần thiết
  4. Hiểu rõ về các rủi ro và tuân thủ quy định pháp luật về giao dịch tiền điện tử

Cuối cùng, việc kết hợp nhiều chiến lược khác nhau có thể giúp đa dạng hóa rủi ro và tăng cơ hội thành công trong thị trường tiền điện tử.

📊 Phân Tích Rủi Ro và Lợi Nhuận Danh Mục Đầu Tư (Portfolio)

· 19 min read

📊 Phân Tích Rủi Ro và Lợi Nhuận Danh Mục Đầu Tư (Portfolio) với Python

Phân tích danh mục đầu tư

Giới thiệu

Quản lý danh mục đầu tư (portfolio) hiệu quả đòi hỏi sự cân bằng giữa rủi ro và lợi nhuận kỳ vọng. Trong bài viết này, chúng ta sẽ tìm hiểu cách sử dụng Python để phân tích, đánh giá và tối ưu hóa danh mục đầu tư chứng khoán, từ việc thu thập dữ liệu, tính toán các chỉ số rủi ro-lợi nhuận, cho đến việc áp dụng lý thuyết danh mục đầu tư hiện đại (Modern Portfolio Theory) của Harry Markowitz.

Những công cụ cần thiết

# Thư viện cần cài đặt
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import yfinance as yf
import scipy.optimize as sco
from scipy import stats
import cvxpy as cp
import warnings

# Thiết lập hiển thị
warnings.filterwarnings('ignore')
plt.style.use('fivethirtyeight')
np.random.seed(777)

Thu thập dữ liệu

Sử dụng Yahoo Finance API

Bước đầu tiên trong phân tích danh mục đầu tư là thu thập dữ liệu lịch sử. Chúng ta sẽ sử dụng thư viện yfinance để tải dữ liệu từ Yahoo Finance:

def get_stock_data(tickers, start_date, end_date, interval='1d'):
"""
Thu thập dữ liệu cổ phiếu từ Yahoo Finance

Tham số:
tickers (list): Danh sách mã cổ phiếu
start_date (str): Ngày bắt đầu (YYYY-MM-DD)
end_date (str): Ngày kết thúc (YYYY-MM-DD)
interval (str): Khoảng thời gian ('1d', '1wk', '1mo')

Trả về:
pd.DataFrame: DataFrame chứa giá đóng cửa đã điều chỉnh của các cổ phiếu
"""
data = yf.download(tickers, start=start_date, end=end_date, interval=interval)['Adj Close']

# Xử lý trường hợp chỉ có một mã cổ phiếu
if isinstance(data, pd.Series):
data = pd.DataFrame(data)
data.columns = [tickers]

# Kiểm tra và xử lý dữ liệu thiếu
if data.isnull().sum().sum() > 0:
print(f"Có {data.isnull().sum().sum()} giá trị thiếu. Tiến hành điền giá trị thiếu...")
data = data.fillna(method='ffill').fillna(method='bfill')

return data

Ví dụ thu thập dữ liệu cho một số cổ phiếu

# Danh sách các mã cổ phiếu mẫu (đổi thành các mã trên HOSE nếu cần)
tickers = ['AAPL', 'MSFT', 'GOOG', 'AMZN', 'META', 'TSLA', 'NVDA', 'JPM', 'V', 'PG']

# Khoảng thời gian
start_date = '2018-01-01'
end_date = '2023-01-01'

# Thu thập dữ liệu
prices = get_stock_data(tickers, start_date, end_date)
print(prices.head())

# Vẽ biểu đồ giá cổ phiếu (chuẩn hóa)
normalized_prices = prices / prices.iloc[0] * 100
plt.figure(figsize=(12, 8))
normalized_prices.plot()
plt.title('Diễn biến giá cổ phiếu (chuẩn hóa)')
plt.xlabel('Ngày')
plt.ylabel('Giá chuẩn hóa (100 = giá ban đầu)')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()

Tính toán lợi nhuận

Tính lợi nhuận hàng ngày và thống kê mô tả

def calculate_returns(prices, period='daily'):
"""
Tính lợi nhuận của cổ phiếu

Tham số:
prices (pd.DataFrame): DataFrame chứa giá cổ phiếu
period (str): Kỳ hạn lợi nhuận ('daily', 'weekly', 'monthly', 'annual')

Trả về:
pd.DataFrame: DataFrame chứa lợi nhuận
"""
if period == 'daily':
returns = prices.pct_change().dropna()
elif period == 'weekly':
returns = prices.resample('W').last().pct_change().dropna()
elif period == 'monthly':
returns = prices.resample('M').last().pct_change().dropna()
elif period == 'annual':
returns = prices.resample('Y').last().pct_change().dropna()
else:
raise ValueError("Kỳ hạn không hợp lệ. Sử dụng 'daily', 'weekly', 'monthly', hoặc 'annual'")

return returns
# Tính lợi nhuận hàng ngày
daily_returns = calculate_returns(prices)

# Thống kê mô tả
desc_stats = daily_returns.describe().T
desc_stats['annualized_return'] = daily_returns.mean() * 252
desc_stats['annualized_vol'] = daily_returns.std() * np.sqrt(252)
desc_stats['sharpe_ratio'] = desc_stats['annualized_return'] / desc_stats['annualized_vol']

print(desc_stats[['mean', 'std', 'annualized_return', 'annualized_vol', 'sharpe_ratio']])

Biểu đồ phân phối lợi nhuận

def plot_returns_distribution(returns):
"""
Vẽ biểu đồ phân phối lợi nhuận

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
"""
plt.figure(figsize=(15, 10))

for i, ticker in enumerate(returns.columns):
plt.subplot(3, 4, i+1)

# Histogram
sns.histplot(returns[ticker], kde=True, stat="density", linewidth=0)

# Normal distribution curve
xmin, xmax = plt.xlim()
x = np.linspace(xmin, xmax, 100)
p = stats.norm.pdf(x, returns[ticker].mean(), returns[ticker].std())
plt.plot(x, p, 'k', linewidth=2)

plt.title(f'Phân phối lợi nhuận {ticker}')
plt.xlabel('Lợi nhuận hàng ngày')
plt.ylabel('Mật độ')

plt.tight_layout()

Phân tích rủi ro

Tính toán các thước đo rủi ro

def calculate_risk_metrics(returns, risk_free_rate=0.0):
"""
Tính toán các thước đo rủi ro cho từng cổ phiếu

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
risk_free_rate (float): Lãi suất phi rủi ro (annualized)

Trả về:
pd.DataFrame: DataFrame chứa các thước đo rủi ro
"""
# Chuyển đổi lãi suất phi rủi ro sang tỷ lệ hàng ngày
daily_rf = (1 + risk_free_rate) ** (1/252) - 1

# DataFrame để lưu kết quả
metrics = pd.DataFrame(index=returns.columns)

# Độ biến động (Volatility) hàng năm
metrics['volatility'] = returns.std() * np.sqrt(252)

# Tỷ lệ Sharpe
excess_returns = returns.sub(daily_rf, axis=0)
metrics['sharpe_ratio'] = (excess_returns.mean() * 252) / metrics['volatility']

# Maximum Drawdown
cumulative_returns = (1 + returns).cumprod()
rolling_max = cumulative_returns.cummax()
drawdown = (cumulative_returns - rolling_max) / rolling_max
metrics['max_drawdown'] = drawdown.min()

# Value at Risk (VaR) 95%
metrics['var_95'] = returns.quantile(0.05)

# Conditional Value at Risk (CVaR) 95%
metrics['cvar_95'] = returns[returns < returns.quantile(0.05)].mean()

# Tỷ lệ Sortino
negative_returns = returns.copy()
negative_returns[negative_returns > 0] = 0
downside_deviation = negative_returns.std() * np.sqrt(252)
metrics['sortino_ratio'] = (excess_returns.mean() * 252) / downside_deviation

# Beta (so với chỉ số S&P 500)
sp500 = yf.download('^GSPC', start=returns.index[0], end=returns.index[-1], interval='1d')['Adj Close']
sp500_returns = sp500.pct_change().dropna()

# Chỉ lấy những ngày trùng khớp
common_index = returns.index.intersection(sp500_returns.index)
returns_aligned = returns.loc[common_index]
sp500_returns_aligned = sp500_returns.loc[common_index]

# Tính beta
for ticker in returns.columns:
covariance = np.cov(returns_aligned[ticker], sp500_returns_aligned)[0, 1]
variance = np.var(sp500_returns_aligned)
metrics.loc[ticker, 'beta'] = covariance / variance

return metrics

Vẽ biểu đồ rủi ro-lợi nhuận

def plot_risk_return(returns, risk_metrics, period=252):
"""
Vẽ biểu đồ rủi ro-lợi nhuận

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
risk_metrics (pd.DataFrame): DataFrame chứa các thước đo rủi ro
period (int): Số ngày trong một năm để annualize lợi nhuận
"""
plt.figure(figsize=(12, 8))

# Tính lợi nhuận trung bình hàng năm
annual_returns = returns.mean() * period

# Biểu đồ scatter
plt.scatter(risk_metrics['volatility'], annual_returns, s=200, alpha=0.6)

# Thêm nhãn
for i, ticker in enumerate(returns.columns):
plt.annotate(ticker,
(risk_metrics['volatility'][i], annual_returns[i]),
xytext=(10, 5),
textcoords='offset points',
fontsize=12)

# Thêm title và label
plt.title('Biểu đồ Rủi ro - Lợi nhuận', fontsize=16)
plt.xlabel('Rủi ro (Độ biến động hàng năm)', fontsize=14)
plt.ylabel('Lợi nhuận kỳ vọng hàng năm', fontsize=14)

# Thêm đường Linear Regression
z = np.polyfit(risk_metrics['volatility'], annual_returns, 1)
p = np.poly1d(z)
plt.plot(risk_metrics['volatility'], p(risk_metrics['volatility']), "r--", linewidth=2)

plt.tight_layout()

Vẽ biểu đồ Drawdown

def plot_drawdown(returns):
"""
Vẽ biểu đồ drawdown cho từng cổ phiếu

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
"""
plt.figure(figsize=(12, 8))

for ticker in returns.columns:
# Tính cumulative returns
cumulative_returns = (1 + returns[ticker]).cumprod()

# Tính rolling maximum
rolling_max = cumulative_returns.cummax()

# Tính drawdown
drawdown = (cumulative_returns - rolling_max) / rolling_max

# Vẽ drawdown
plt.plot(drawdown, label=ticker)

plt.title('Biểu đồ Drawdown', fontsize=16)
plt.xlabel('Ngày', fontsize=14)
plt.ylabel('Drawdown', fontsize=14)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()

Ma trận tương quan và phân tích đa dạng hóa

Tính ma trận tương quan và hiển thị heatmap

def plot_correlation_matrix(returns):
"""
Vẽ ma trận tương quan giữa các cổ phiếu

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
"""
# Tính ma trận tương quan
corr_matrix = returns.corr()

# Thiết lập kích thước biểu đồ
plt.figure(figsize=(10, 8))

# Vẽ heatmap
cmap = sns.diverging_palette(220, 10, as_cmap=True)
sns.heatmap(corr_matrix, annot=True, cmap=cmap, center=0,
square=True, linewidths=.5, cbar_kws={"shrink": .8})

plt.title('Ma trận tương quan giữa các cổ phiếu', fontsize=16)
plt.tight_layout()

Phân tích đa dạng hóa danh mục

def calculate_portfolio_performance(weights, returns):
"""
Tính toán hiệu suất của danh mục đầu tư

Tham số:
weights (np.array): Trọng số phân bổ cho từng cổ phiếu
returns (pd.DataFrame): DataFrame chứa lợi nhuận

Trả về:
tuple: (lợi nhuận kỳ vọng, độ biến động, tỷ lệ Sharpe)
"""
# Lợi nhuận danh mục
portfolio_return = np.sum(returns.mean() * weights) * 252

# Độ biến động danh mục
portfolio_volatility = np.sqrt(np.dot(weights.T, np.dot(returns.cov() * 252, weights)))

# Tỷ lệ Sharpe
sharpe_ratio = portfolio_return / portfolio_volatility

return portfolio_return, portfolio_volatility, sharpe_ratio

Phân tích hiệu quả đa dạng hóa ngẫu nhiên

def random_portfolios(returns, num_portfolios=10000):
"""
Tạo ngẫu nhiên các danh mục đầu tư và tính hiệu suất

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
num_portfolios (int): Số lượng danh mục ngẫu nhiên cần tạo

Trả về:
tuple: (results, weights) - kết quả hiệu suất và trọng số tương ứng
"""
results = np.zeros((num_portfolios, 3))
weights_record = np.zeros((num_portfolios, len(returns.columns)))

for i in range(num_portfolios):
# Tạo trọng số ngẫu nhiên
weights = np.random.random(len(returns.columns))
weights /= np.sum(weights)
weights_record[i, :] = weights

# Tính hiệu suất
results[i, 0], results[i, 1], results[i, 2] = calculate_portfolio_performance(weights, returns)

return results, weights_record

Vẽ biểu đồ đường biên hiệu quả (Efficient Frontier)

def plot_efficient_frontier(returns, results, weights_record):
"""
Vẽ biểu đồ đường biên hiệu quả

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
results (np.array): Mảng kết quả hiệu suất của các danh mục ngẫu nhiên
weights_record (np.array): Mảng trọng số của các danh mục ngẫu nhiên
"""
plt.figure(figsize=(12, 8))

# Vẽ các danh mục ngẫu nhiên
plt.scatter(results[:, 1], results[:, 0], c=results[:, 2],
cmap='viridis', marker='o', s=10, alpha=0.3)

# Đánh dấu danh mục có Sharpe ratio cao nhất
max_sharpe_idx = np.argmax(results[:, 2])
max_sharpe_portfolio = results[max_sharpe_idx]
plt.scatter(max_sharpe_portfolio[1], max_sharpe_portfolio[0],
marker='*', color='r', s=500, label='Danh mục tối ưu theo Sharpe')

# Đánh dấu danh mục có độ biến động thấp nhất
min_vol_idx = np.argmin(results[:, 1])
min_vol_portfolio = results[min_vol_idx]
plt.scatter(min_vol_portfolio[1], min_vol_portfolio[0],
marker='*', color='g', s=500, label='Danh mục có độ biến động thấp nhất')

# Đánh dấu cổ phiếu riêng lẻ
for i, ticker in enumerate(returns.columns):
individual_return = returns.mean()[i] * 252
individual_volatility = returns.std()[i] * np.sqrt(252)
plt.scatter(individual_volatility, individual_return, marker='o', s=200,
color='black', label=ticker if i == 0 else "")
plt.annotate(ticker, (individual_volatility, individual_return),
xytext=(10, 5), textcoords='offset points')

# Thêm title và label
plt.colorbar(label='Sharpe ratio')
plt.title('Đường biên hiệu quả (Efficient Frontier)', fontsize=16)
plt.xlabel('Độ biến động (Rủi ro)', fontsize=14)
plt.ylabel('Lợi nhuận kỳ vọng', fontsize=14)
plt.legend()

# Hiển thị thông tin về danh mục tối ưu
print("Danh mục tối ưu theo tỷ lệ Sharpe:")
print(f"Lợi nhuận kỳ vọng: {max_sharpe_portfolio[0]:.4f}")
print(f"Độ biến động: {max_sharpe_portfolio[1]:.4f}")
print(f"Tỷ lệ Sharpe: {max_sharpe_portfolio[2]:.4f}")
print("\nPhân bổ vốn:")
for i, ticker in enumerate(returns.columns):
print(f"{ticker}: {weights_record[max_sharpe_idx, i] * 100:.2f}%")

plt.tight_layout()

Tối ưu hóa danh mục đầu tư

Tìm danh mục tối ưu theo lý thuyết Markowitz

def optimize_portfolio(returns, risk_free_rate=0.0, target_return=None):
"""
Tìm danh mục đầu tư tối ưu sử dụng lý thuyết Markowitz

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
risk_free_rate (float): Lãi suất phi rủi ro (annualized)
target_return (float): Lợi nhuận mục tiêu (annualized), nếu None thì tối đa hóa Sharpe ratio

Trả về:
tuple: (optimal_weights, expected_return, volatility, sharpe_ratio)
"""
n = len(returns.columns)
returns_mean = returns.mean() * 252
cov_matrix = returns.cov() * 252

# Khai báo biến
w = cp.Variable(n)

# Khai báo hàm mục tiêu
if target_return is None:
# Tối đa hóa tỷ lệ Sharpe
risk = cp.quad_form(w, cov_matrix)
ret = returns_mean @ w
sharpe = (ret - risk_free_rate) / cp.sqrt(risk)
objective = cp.Maximize(sharpe)
else:
# Tối thiểu hóa rủi ro với lợi nhuận mục tiêu
risk = cp.quad_form(w, cov_matrix)
objective = cp.Minimize(risk)

# Ràng buộc
constraints = [
cp.sum(w) == 1, # Tổng trọng số bằng 1
w >= 0 # Không cho phép bán khống
]

# Thêm ràng buộc về lợi nhuận mục tiêu nếu cần
if target_return is not None:
constraints.append(returns_mean @ w >= target_return)

# Giải bài toán tối ưu
problem = cp.Problem(objective, constraints)
problem.solve()

# Lấy kết quả
optimal_weights = w.value
expected_return = returns_mean.dot(optimal_weights)
volatility = np.sqrt(optimal_weights.T @ cov_matrix @ optimal_weights)
sharpe_ratio = (expected_return - risk_free_rate) / volatility

return optimal_weights, expected_return, volatility, sharpe_ratio

Vẽ đường biên hiệu quả lý thuyết

def plot_theoretical_efficient_frontier(returns, risk_free_rate=0.0, points=100):
"""
Vẽ đường biên hiệu quả lý thuyết

Tham số:
returns (pd.DataFrame): DataFrame chứa lợi nhuận
risk_free_rate (float): Lãi suất phi rủi ro (annualized)
points (int): Số điểm để vẽ đường biên hiệu quả
"""
plt.figure(figsize=(12, 8))

# Tính danh mục có độ biến động thấp nhất
min_vol_weights, min_vol_return, min_vol_risk, _ = optimize_portfolio(returns, risk_free_rate, target_return=None)

# Tính danh mục có tỷ lệ Sharpe cao nhất
max_sharpe_weights, max_sharpe_return, max_sharpe_risk, max_sharpe = optimize_portfolio(returns, risk_free_rate)

# Tính các danh mục tối ưu với lợi nhuận mục tiêu khác nhau
target_returns = np.linspace(min_vol_return, max(returns.mean()) * 252 * 1.2, points)
efficient_risk = []
efficient_return = []

for target in target_returns:
try:
weights, ret, risk, _ = optimize_portfolio(returns, risk_free_rate, target_return=target)
efficient_risk.append(risk)
efficient_return.append(ret)
except:
pass

# Vẽ đường biên hiệu quả
plt.plot(efficient_risk, efficient_return, 'b-', linewidth=3, label='Đường biên hiệu quả')

# Đánh dấu danh mục có độ biến động thấp nhất
plt.scatter(min_vol_risk, min_vol_return, marker='*', color='g', s=500,
label='Danh mục có độ biến động thấp nhất')

# Đánh dấu danh mục có tỷ lệ Sharpe cao nhất
plt.scatter(max_sharpe_risk, max_sharpe_return, marker='*', color='r', s=500,
label='Danh mục tối ưu theo Sharpe')

# Vẽ đường CML (Capital Market Line)
x_cml = np.linspace(0, max(efficient_risk) * 1.2, 100)
y_cml = risk_free_rate + x_cml * (max_sharpe_return - risk_free_rate) / max_sharpe_risk
plt.plot(x_cml, y_cml, 'r--', label='CML')

# Đánh dấu cổ phiếu riêng lẻ
for i, ticker in enumerate(returns.columns):
individual_return = returns.mean()[i] * 252
individual_volatility = returns.std()[i] * np.sqrt(252)
plt.scatter(individual_volatility, individual_return, marker='o', s=200,
color='black')
plt.annotate(ticker, (individual_volatility, individual_return),
xytext=(10, 5), textcoords='offset points')

# Thêm title và label
plt.title('Đường biên hiệu quả lý thuyết', fontsize=16)
plt.xlabel('Độ biến động (Rủi ro)', fontsize=14)
plt.ylabel('Lợi nhuận kỳ vọng', fontsize=14)
plt.legend()

# Hiển thị thông tin về danh mục tối ưu
print("Danh mục tối ưu theo tỷ lệ Sharpe:")
print(f"Lợi nhuận kỳ vọng: {max_sharpe_return:.4f}")
print(f"Độ biến động: {max_sharpe_risk:.4f}")
print(f"Tỷ lệ Sharpe: {max_sharpe:.4f}")
print("\nPhân bổ vốn:")
for i, ticker in enumerate(returns.columns):
print(f"{ticker}: {max_sharpe_weights[i] * 100:.2f}%")

plt.tight_layout()

Đánh giá hiệu suất danh mục đầu tư trong quá khứ

Mô phỏng hiệu suất danh mục theo thời gian

def simulate_portfolio_performance(weights, prices):
"""
Mô phỏng hiệu suất danh mục theo thời gian

Tham số:
weights (np.array): Trọng số phân bổ cho từng cổ phiếu
prices (pd.DataFrame): DataFrame chứa giá cổ phiếu

Trả về:
pd.Series: Series chứa giá trị danh mục theo thời gian
"""
# Chuẩn hóa giá
normalized_prices = prices / prices.iloc[0]

# Tính giá trị danh mục
portfolio_value = (normalized_prices * weights).sum(axis=1)

return portfolio_value

So sánh hiệu suất với chỉ số thị trường

def compare_with_benchmark(portfolio_value, start_date, end_date, benchmark='^GSPC'):
"""
So sánh hiệu suất của danh mục với chỉ số thị trường

Tham số:
portfolio_value (pd.Series): Series chứa giá trị danh mục
start_date (str): Ngày bắt đầu (YYYY-MM-DD)
end_date (str): Ngày kết thúc (YYYY-MM-DD)
benchmark (str): Mã chỉ số thị trường (mặc định là S&P 500)

Trả về:
tuple: (portfolio_return, benchmark_return)
"""
# Tải dữ liệu chỉ số
benchmark_data = yf.download(benchmark, start=start_date, end=end_date)['Adj Close']

# Chuẩn hóa giá trị
normalized_benchmark = benchmark_data / benchmark_data.iloc[0]
normalized_portfolio = portfolio_value / portfolio_value.iloc[0]

# Vẽ biểu đồ
plt.figure(figsize=(12, 8))
plt.plot(normalized_portfolio, label='Danh mục của bạn')
plt.plot(normalized_benchmark, label=f'Chỉ số {benchmark}')
plt.title('So sánh hiệu suất với chỉ số thị trường', fontsize=16)
plt.xlabel('Ngày', fontsize=14)
plt.ylabel('Giá trị (chuẩn hóa)', fontsize=14)
plt.legend()
plt.grid(True, alpha=0.3)

# Tính toán lợi nhuận tổng thể
portfolio_return = normalized_portfolio.iloc[-1] - 1
benchmark_return = normalized_benchmark.iloc[-1] - 1

# Thông tin về alpha và beta
portfolio_returns = normalized_portfolio.pct_change().dropna()
benchmark_returns = normalized_benchmark.pct_change().dropna()

# Chỉ lấy những ngày trùng khớp
common_index = portfolio_returns.index.intersection(benchmark_returns.index)
portfolio_returns = portfolio_returns.loc[common_index]
benchmark_returns = benchmark_returns.loc[common_index]

# Tính beta
covariance = np.cov(portfolio_returns, benchmark_returns)[0, 1]
variance = np.var(benchmark_returns)
beta = covariance / variance

# Tính alpha (Jensen's Alpha)
risk_free_rate = 0.0 # Có thể thay đổi tùy vào lãi suất thực tế
expected_return = risk_free_rate + beta * (benchmark_returns.mean() * 252 - risk_free_rate)
alpha = portfolio_returns.mean() * 252 - expected_return

print(f"Lợi nhuận danh mục: {portfolio_return:.4f} ({portfolio_return * 100:.2f}%)")
print(f"Lợi nhuận chỉ số {benchmark}: {benchmark_return:.4f} ({benchmark_return * 100:.2f}%)")
print(f"Alpha: {alpha:.4f}")
print(f"Beta: {beta:.4f}")

plt.tight_layout()

return portfolio_return, benchmark_return

Kiểm định sức chịu đựng (Stress Testing)

Phân tích kịch bản (Scenario Analysis)

def stress_test_scenarios(weights, returns, scenarios):
"""
Phân tích kịch bản stress test

Tham số:
weights (np.array): Trọng số phân bổ cho từng cổ phiếu
returns (pd.DataFrame): DataFrame chứa lợi nhuận
scenarios (dict): Dictionary chứa các kịch bản stress test
{'tên kịch bản': [start_date, end_date]}

Trả về:
pd.DataFrame: DataFrame chứa kết quả stress test
"""
results = pd.DataFrame(columns=['scenario', 'portfolio_return', 'max_drawdown'])

for scenario_name, (start_date, end_date) in scenarios.items():
# Lấy dữ liệu theo kịch bản
scenario_data = returns.loc[start_date:end_date]

# Tính lợi nhuận danh mục trong kịch bản
portfolio_returns = (scenario_data * weights).sum(axis=1)

# Tính cumulative returns
cumulative_returns = (1 + portfolio_returns).cumprod()

# Tính max drawdown
rolling_max = cumulative_returns.cummax()
drawdown = (cumulative_returns - rolling_max) / rolling_max
max_drawdown = drawdown.min()

# Tính tổng lợi nhuận
total_return = (1 + portfolio_returns).prod() - 1

results = results.append({
'scenario': scenario_name,
'portfolio_return': total_return,
'max_drawdown': max_drawdown
}, ignore_index=True)

return results

Phân tích Monte Carlo

def monte_carlo_simulation(weights, returns, n_simulations=1000, time_horizon=252):
"""
Thực hiện mô phỏng Monte Carlo cho danh mục đầu tư

Tham số:
weights (np.array): Trọng số phân bổ cho từng cổ phiếu
returns (pd.DataFrame): DataFrame chứa lợi nhuận
n_simulations (int): Số lần mô phỏng
time_horizon (int): Khoảng thời gian mô phỏng (ngày giao dịch)

Trả về:
np.array: Mảng kết quả mô phỏng
"""
# Tính mean và covariance matrix
mean_returns = returns.mean()
cov_matrix = returns.cov()

# Tính lợi nhuận danh mục
portfolio_mean = np.sum(mean_returns * weights)
portfolio_var = np.dot(weights.T, np.dot(cov_matrix, weights))
portfolio_std = np.sqrt(portfolio_var)

# Mô phỏng
simulations = np.zeros((n_simulations, time_horizon))

for i in range(n_simulations):
# Tạo chuỗi lợi nhuận ngẫu nhiên
Z = np.random.normal(portfolio_mean, portfolio_std, time_horizon)
# Tính cumulative returns
simulations[i] = np.cumprod(1 + Z) - 1

# Vẽ biểu đồ
plt.figure(figsize=(12, 8))

for i in range(n_simulations):
plt.plot(simulations[i], linewidth=0.5, alpha=0.1, color='blue')

# Tính các phân vị
percentiles = [10, 50, 90]
percentile_data = np.percentile(simulations, percentiles, axis=0)

for i, p in enumerate(percentiles):
plt.plot(percentile_data[i], linewidth=2,
label=f'Phân vị thứ {p}',
color='red' if p == 50 else 'black')

plt.title('Mô phỏng Monte Carlo', fontsize=16)
plt.xlabel('Ngày', fontsize=14)
plt.ylabel('Lợi nhuận tích lũy', fontsize=14)
plt.legend()
plt.grid(True, alpha=0.3)

# Tính kết quả
final_returns = simulations[:, -1]

print(f"Lợi nhuận kỳ vọng sau {time_horizon} ngày: {np.mean(final_returns):.4f} ({np.mean(final_returns) * 100:.2f}%)")
print(f"VaR (95%): {np.percentile(final_returns, 5):.4f} ({np.percentile(final_returns, 5) * 100:.2f}%)")
print(f"VaR (99%): {np.percentile(final_returns, 1):.4f} ({np.percentile(final_returns, 1) * 100:.2f}%)")

plt.tight_layout()

return simulations

Tái cân bằng danh mục đầu tư

Mô phỏng tái cân bằng định kỳ

def simulate_rebalancing(weights, prices, rebalance_frequency='M'):
"""
Mô phỏng hiệu suất danh mục với tái cân bằng định kỳ

Tham số:
weights (np.array): Trọng số ban đầu cho từng cổ phiếu
prices (pd.DataFrame): DataFrame chứa giá cổ phiếu
rebalance_frequency (str): Tần suất tái cân bằng ('D', 'W', 'M', 'Q', 'Y')

Trả về:
tuple: (rebalanced_portfolio, buy_hold_portfolio) - hiệu suất danh mục tái cân bằng và mua giữ
"""
# Ban đầu giả sử có 1 đơn vị tiền
initial_investment = 1.0

# Tính số lượng cổ phiếu ban đầu
initial_prices = prices.iloc[0]
shares = np.array(weights) * initial_investment / initial_prices

# Khởi tạo các biến theo dõi
portfolio_value = pd.Series(index=prices.index)
buy_hold_value = pd.Series(index=prices.index)

# Tính giá trị danh mục theo thời gian
for date in prices.index:
# Giá trị hiện tại của danh mục
current_value = np.sum(shares * prices.loc[date])
portfolio_value[date] = current_value

# Nếu là ngày cần tái cân bằng và không phải ngày đầu tiên
if date != prices.index[0]:
if rebalance_frequency == 'D':
rebalance = True
elif rebalance_frequency == 'W' and date.dayofweek == 0: # Thứ 2
rebalance = True
elif rebalance_frequency == 'M' and date.day == 1: # Ngày đầu tháng
rebalance = True
elif rebalance_frequency == 'Q' and date.month in [1, 4, 7, 10] and date.day == 1:
rebalance = True
elif rebalance_frequency == 'Y' and date.month == 1 and date.day == 1:
rebalance = True
else:
rebalance = False

if rebalance:
# Tính trọng số hiện tại
current_weights = shares * prices.loc[date] / current_value

# Nếu chênh lệch đáng kể so với trọng số mục tiêu, thực hiện tái cân bằng
if np.max(np.abs(current_weights - weights)) > 0.01: # 1% threshold
# Tái cân bằng
shares = np.array(weights) * current_value / prices.loc[date]

# Mô phỏng danh mục mua và giữ (không tái cân bằng)
buy_hold = (prices / prices.iloc[0] * weights).sum(axis=1)

# Vẽ biểu đồ so sánh
plt.figure(figsize=(12, 8))
plt.plot(portfolio_value / portfolio_value.iloc[0], label=f'Danh mục tái cân bằng ({rebalance_frequency})')
plt.plot(buy_hold, label='Danh mục mua và giữ')
plt.title('So sánh hiệu suất: Tái cân bằng vs Mua và giữ', fontsize=16)
plt.xlabel('Ngày', fontsize=14)
plt.ylabel('Giá trị (chuẩn hóa)', fontsize=14)
plt.legend()
plt.grid(True, alpha=0.3)

# Tính toán lợi nhuận tổng thể
rebalance_return = portfolio_value.iloc[-1] / portfolio_value.iloc[0] - 1
buy_hold_return = buy_hold.iloc[-1] - 1

print(f"Lợi nhuận danh mục tái cân bằng: {rebalance_return:.4f} ({rebalance_return * 100:.2f}%)")
print(f"Lợi nhuận danh mục mua và giữ: {buy_hold_return:.4f} ({buy_hold_return * 100:.2f}%)")

plt.tight_layout()

return portfolio_value / portfolio_value.iloc[0], buy_hold

Ứng dụng thực tế

Ví dụ tổng hợp phân tích danh mục đầu tư

def complete_portfolio_analysis(tickers, start_date, end_date):
"""
Thực hiện phân tích danh mục đầu tư toàn diện

Tham số:
tickers (list): Danh sách mã cổ phiếu
start_date (str): Ngày bắt đầu (YYYY-MM-DD)
end_date (str): Ngày kết thúc (YYYY-MM-DD)

Trả về:
dict: Dictionary chứa thông tin về danh mục tối ưu
"""
# Thu thập dữ liệu
prices = get_stock_data(tickers, start_date, end_date)
returns = calculate_returns(prices)

# Tính toán các thước đo rủi ro
risk_metrics = calculate_risk_metrics(returns)

# Vẽ biểu đồ rủi ro-lợi nhuận
plot_risk_return(returns, risk_metrics)

# Vẽ ma trận tương quan
plot_correlation_matrix(returns)

# Tìm danh mục tối ưu
optimal_weights, expected_return, volatility, sharpe_ratio = optimize_portfolio(returns)

# Vẽ đường biên hiệu quả lý thuyết
plot_theoretical_efficient_frontier(returns)

# Mô phỏng hiệu suất danh mục
portfolio_value = simulate_portfolio_performance(optimal_weights, prices)

# So sánh với chỉ số thị trường
compare_with_benchmark(portfolio_value, start_date, end_date)

# Mô phỏng tái cân bằng
simulate_rebalancing(optimal_weights, prices, rebalance_frequency='M')

# Mô phỏng Monte Carlo
monte_carlo_simulation(optimal_weights, returns)

# Kết quả
result = {
'optimal_weights': dict(zip(tickers, optimal_weights)),
'expected_return': expected_return,
'volatility': volatility,
'sharpe_ratio': sharpe_ratio
}

return result

Kết luận

Trong bài viết này, chúng ta đã tìm hiểu cách sử dụng Python để thực hiện phân tích rủi ro và lợi nhuận danh mục đầu tư. Từ việc thu thập dữ liệu, tính toán các thước đo rủi ro, xây dựng mô hình tối ưu hóa danh mục theo lý thuyết Markowitz, cho đến kiểm định sức chịu đựng và tái cân bằng danh mục.

Các phương pháp và công cụ này giúp nhà đầu tư ra quyết định đầu tư dựa trên dữ liệu, cân bằng giữa rủi ro và lợi nhuận kỳ vọng, từ đó xây dựng chiến lược đầu tư hiệu quả và phù hợp với mục tiêu tài chính.

Lưu ý rằng kết quả phân tích dựa trên dữ liệu lịch sử không đảm bảo hiệu suất trong tương lai. Nhà đầu tư nên kết hợp các phương pháp phân tích khác và cập nhật chiến lược định kỳ để thích ứng với điều kiện thị trường thay đổi.

Tài liệu tham khảo

  1. Markowitz, H. (1952). Portfolio Selection. The Journal of Finance, 7(1), 77-91.
  2. Sharpe, W. F. (1964). Capital Asset Prices: A Theory of Market Equilibrium under Conditions of Risk. The Journal of Finance, 19(3), 425-442.
  3. Hull, J. C. (2018). Risk Management and Financial Institutions (5th ed.). Wiley.
  4. Python for Finance: Mastering Data-Driven Finance (2nd ed.) by Yves Hilpisch
  5. Yahoo Finance API Documentation: https://pypi.org/project/yfinance/

Giao Dịch Định Lượng: Từ Lý Thuyết Đến Thực Hành

· 4 min read
admin

Giới thiệu

Giao dịch định lượng (Quantitative Trading) là phương pháp giao dịch sử dụng các mô hình toán học và thuật toán để đưa ra quyết định giao dịch. Trong bài viết này, chúng ta sẽ tìm hiểu chi tiết về giao dịch định lượng, từ lý thuyết đến thực hành.

Quy trình giao dịch định lượng

Giao dịch định lượng là gì?

Giao dịch định lượng là việc sử dụng các phương pháp toán học, thống kê và lập trình để:

  • Phân tích dữ liệu thị trường
  • Xây dựng chiến lược giao dịch
  • Tự động hóa quá trình giao dịch
  • Quản lý rủi ro

Các thành phần cốt lõi

1. Phân tích dữ liệu

  • Thu thập dữ liệu thị trường
  • Xử lý và làm sạch dữ liệu
  • Phân tích thống kê
  • Tìm kiếm các mẫu hình

Phân tích dữ liệu thị trường

2. Xây dựng chiến lược

  • Phát triển ý tưởng giao dịch
  • Viết code backtesting
  • Tối ưu hóa tham số
  • Đánh giá hiệu suất

Backtesting chiến lược

3. Triển khai thực tế

  • Kết nối với sàn giao dịch
  • Tự động hóa giao dịch
  • Quản lý rủi ro
  • Giám sát hiệu suất

Ví dụ thực tế với Python

1. Thu thập dữ liệu

import yfinance as yf
import pandas as pd

# Tải dữ liệu VN30
vn30 = yf.download('^VN30', start='2020-01-01', end='2024-03-21')

# Tính toán các chỉ báo kỹ thuật
vn30['SMA20'] = vn30['Close'].rolling(window=20).mean()
vn30['SMA50'] = vn30['Close'].rolling(window=50).mean()
vn30['RSI'] = calculate_rsi(vn30['Close'])

2. Xây dựng chiến lược

def generate_signals(df):
signals = pd.DataFrame(index=df.index)
signals['signal'] = 0

# Tín hiệu mua khi SMA20 cắt lên SMA50
signals['signal'][df['SMA20'] > df['SMA50']] = 1

# Tín hiệu bán khi SMA20 cắt xuống SMA50
signals['signal'][df['SMA20'] < df['SMA50']] = -1

return signals

3. Backtesting

def backtest_strategy(signals, prices):
positions = signals['signal'].diff()
portfolio = pd.DataFrame(index=signals.index)
portfolio['positions'] = positions
portfolio['holdings'] = positions.cumsum() * prices['Close']
portfolio['cash'] = 100000 - (positions * prices['Close']).cumsum()
portfolio['total'] = portfolio['cash'] + portfolio['holdings']
portfolio['returns'] = portfolio['total'].pct_change()

return portfolio

Các thư viện Python hữu ích

  1. yfinance: Tải dữ liệu thị trường
  2. pandas: Xử lý và phân tích dữ liệu
  3. numpy: Tính toán số học
  4. scipy: Phân tích thống kê
  5. matplotlib: Vẽ đồ thị
  6. backtrader: Backtesting
  7. ta-lib: Chỉ báo kỹ thuật
  8. ccxt: Kết nối với sàn giao dịch

Quản lý rủi ro

Quản lý rủi ro trong giao dịch

1. Position Sizing

  • Xác định kích thước vị thế dựa trên rủi ro
  • Sử dụng công thức Kelly Criterion
  • Đa dạng hóa danh mục

2. Stop Loss

  • Đặt stop loss cho từng giao dịch
  • Sử dụng ATR để xác định mức stop loss
  • Quản lý drawdown

3. Risk Metrics

  • Sharpe Ratio
  • Sortino Ratio
  • Maximum Drawdown
  • Value at Risk (VaR)

Tối ưu hóa chiến lược

Tối ưu hóa chiến lược giao dịch

1. Walk-Forward Analysis

  • Chia dữ liệu thành các giai đoạn
  • Tối ưu trên giai đoạn đầu
  • Kiểm tra trên giai đoạn sau

2. Monte Carlo Simulation

  • Mô phỏng nhiều kịch bản
  • Đánh giá độ ổn định
  • Xác định xác suất thua lỗ

3. Machine Learning

  • Sử dụng các thuật toán ML
  • Feature Engineering
  • Hyperparameter Tuning

Triển khai thực tế

1. Kết nối với sàn giao dịch

import ccxt

exchange = ccxt.binance({
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_SECRET_KEY'
})

# Đặt lệnh
order = exchange.create_market_buy_order('BTC/USDT', 0.1)

2. Giám sát hiệu suất

def monitor_performance(portfolio):
daily_returns = portfolio['returns']
sharpe_ratio = calculate_sharpe_ratio(daily_returns)
max_drawdown = calculate_max_drawdown(portfolio['total'])

return {
'sharpe_ratio': sharpe_ratio,
'max_drawdown': max_drawdown,
'total_return': portfolio['total'][-1] / portfolio['total'][0] - 1
}

Kết luận

Giao dịch định lượng là một lĩnh vực phức tạp nhưng đầy tiềm năng. Để thành công, bạn cần:

  1. Hiểu rõ về thị trường
  2. Có kiến thức về lập trình
  3. Nắm vững các phương pháp thống kê
  4. Có kỷ luật trong quản lý rủi ro
  5. Liên tục học hỏi và cải thiện

Tài liệu tham khảo

  1. "Advances in Financial Machine Learning" - Marcos Lopez de Prado
  2. "Quantitative Trading" - Ernie Chan
  3. "Python for Finance" - Yves Hilpisch
  4. "Algorithmic Trading" - Ernie Chan

Các bước tiếp theo

  1. Học Python và các thư viện cần thiết
  2. Tìm hiểu về thị trường và các công cụ phân tích
  3. Bắt đầu với các chiến lược đơn giản
  4. Tích lũy kinh nghiệm thông qua backtesting
  5. Triển khai dần dần với số tiền nhỏ

Backtest: Khái niệm, các phương pháp và nhận định thực tế

· 5 min read
admin

Backtest là một bước không thể thiếu trong quá trình phát triển chiến lược giao dịch. Tuy nhiên, không phải ai cũng hiểu đúng về backtest và cách áp dụng kết quả backtest vào giao dịch thật. Bài viết này sẽ giúp bạn hiểu rõ:

  • Backtest là gì?
  • Các cách backtest phổ biến (dùng tool, dùng code)
  • Ưu nhược điểm từng phương pháp
  • Những lưu ý khi áp dụng vào thực tế

1. Backtest là gì?

Backtest là quá trình kiểm tra một chiến lược giao dịch hoặc mô hình dự báo trên dữ liệu lịch sử. Mục tiêu là đánh giá xem nếu áp dụng chiến lược đó trong quá khứ thì kết quả sẽ ra sao (lãi/lỗ, drawdown, tỷ lệ thắng...).

Quy trình backtest cơ bản

Các bước cơ bản:

  1. Xây dựng chiến lược/mô hình giao dịch.
  2. Áp dụng lên dữ liệu lịch sử (in-sample).
  3. Đánh giá kết quả: lợi nhuận, drawdown, tỷ lệ thắng, số lệnh, v.v.

2. Các phương pháp backtest

a. Backtest bằng tool (phần mềm)

Ưu điểm:

  • Dễ sử dụng, không cần biết lập trình.
  • Có thể kéo-thả, cấu hình nhanh các chỉ báo, chiến lược.
  • Nhiều tool hỗ trợ trực quan hóa kết quả (biểu đồ, equity curve, thống kê...).

Nhược điểm:

  • Bị giới hạn bởi các chỉ báo, chiến lược có sẵn trong tool.
  • Khó tùy biến các chiến lược phức tạp.
  • Một số tool tính phí hoặc giới hạn tính năng với bản miễn phí.

Một số tool backtest phổ biến:

  • TradingView: Cho phép viết script Pine Script hoặc dùng các indicator có sẵn để backtest.
  • Amibroker: Mạnh về phân tích kỹ thuật, hỗ trợ AFL script.
  • MetaTrader 4/5: Dùng cho Forex, có Strategy Tester.
  • QuantConnect, Quantopian: Nền tảng backtest online cho cổ phiếu, crypto, futures...

Ví dụ minh họa:

  • Bạn có thể vào TradingView, chọn một indicator (ví dụ: RSI), cấu hình chiến lược mua/bán và xem kết quả backtest ngay trên biểu đồ.

b. Backtest bằng code (Python, R, ...)

Ưu điểm:

  • Tùy biến tối đa, có thể xây dựng mọi loại chiến lược từ đơn giản đến phức tạp.
  • Chủ động kiểm soát logic, tính toán, tối ưu hóa.
  • Dễ dàng kết hợp với machine learning, AI, tối ưu tham số...

Nhược điểm:

  • Cần biết lập trình (thường là Python, R).
  • Tốn thời gian xây dựng framework, xử lý dữ liệu, debug.
  • Dễ mắc lỗi logic nếu không kiểm tra kỹ.

Các thư viện backtest phổ biến:

  • Python: backtrader, zipline, bt, pyalgotrade, pandas, numpy, matplotlib...
  • R: quantstrat, blotter, quantmod...

Ví dụ code Python với backtrader:

import backtrader as bt

class SmaCrossStrategy(bt.Strategy):
def __init__(self):
self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=20)

def next(self):
if self.data.close[0] > self.sma[0]:
self.buy()
elif self.data.close[0] < self.sma[0]:
self.sell()

cerebro = bt.Cerebro()
data = bt.feeds.YahooFinanceData(dataname='AAPL', fromdate=datetime(2020,1,1), todate=datetime(2021,1,1))
cerebro.adddata(data)
cerebro.addstrategy(SmaCrossStrategy)
cerebro.run()
cerebro.plot()

3. Ưu nhược điểm của backtest

Ưu điểm:

  • Giúp loại bỏ các chiến lược yếu kém trước khi áp dụng thực tế.
  • Đánh giá được hiệu quả, rủi ro, drawdown, số lệnh, v.v.
  • Tiết kiệm thời gian, chi phí so với forward test.

Nhược điểm:

  • Không đảm bảo kết quả tương lai: Thị trường luôn thay đổi, backtest chỉ là "giả lập quá khứ".
  • Nguy cơ overfitting: Tối ưu quá mức cho dữ liệu cũ, mô hình không hiệu quả với dữ liệu mới.
  • Không tính hết yếu tố thực tế: Phí giao dịch, trượt giá, thanh khoản, độ trễ lệnh...
  • Dữ liệu lịch sử có thể không phản ánh đúng thực tế giao dịch (ví dụ: dữ liệu không có tick-by-tick, không có gap giá, ...).

Minh họa overfitting


4. Kiểm tra out-of-sample và forward test

Out-of-sample là dữ liệu chưa từng dùng để xây dựng hoặc tối ưu mô hình.
Forward test là kiểm tra mô hình trên dữ liệu mới, thời gian thực.

Kiểm tra out-of-sample

Quy trình chuẩn:

  1. Chia dữ liệu thành in-sample (huấn luyện, tối ưu) và out-of-sample (kiểm tra).
  2. Chỉ đánh giá mô hình trên out-of-sample mới biết khả năng tổng quát hóa.
  3. Sau khi backtest, nên forward test trên tài khoản demo hoặc nhỏ để kiểm tra thực tế.

5. Nhận định khi áp dụng vào giao dịch thật

  • Backtest chỉ là bước đầu: Đừng kỳ vọng kết quả backtest sẽ lặp lại 100% trong thực tế.
  • Luôn kiểm tra out-of-sample và forward test.
  • Tối ưu vừa phải, tránh overfitting.
  • Tính đến các yếu tố thực tế: Phí, trượt giá, thanh khoản, tâm lý giao dịch...
  • Giao dịch thật cần quản trị rủi ro chặt chẽ, không nên all-in chỉ vì backtest đẹp.
  • Nên bắt đầu với tài khoản nhỏ, tăng dần khi đã kiểm chứng thực tế.

6. Kết luận

Backtest là công cụ mạnh mẽ để phát triển và đánh giá chiến lược giao dịch, nhưng không phải "chén thánh". Hãy sử dụng backtest một cách thông minh, kết hợp với kiểm tra out-of-sample, forward test và quản trị rủi ro thực tế để thành công lâu dài.


Tài liệu tham khảo

  1. Backtesting Systematic Trading Strategies in Python
  2. Overfitting in Trading Models
  3. Out-of-Sample Testing
  4. Backtrader Documentation
  5. TradingView Backtest

Xử Lý File và API Tài Chính trong Python

· 3 min read
admin

Làm việc với dữ liệu là kỹ năng quan trọng khi lập trình bot trading hoặc các ứng dụng tài chính. Bạn cần biết cách đọc/ghi file, xử lý ngoại lệ và lấy dữ liệu từ API tài chính. Bài viết này sẽ hướng dẫn bạn từng bước, kèm ví dụ thực tế và bài tập tự luyện.

Tổng quan xử lý file &amp; API tài chính


1. Đọc/Ghi File trong Python

Đọc/ghi file trong Python

a. Đọc file văn bản

with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)

b. Ghi file văn bản

with open('output.txt', 'w', encoding='utf-8') as f:
f.write("Hello, Python!\nDữ liệu tài chính...")

c. Đọc file CSV (dữ liệu tài chính thường ở dạng này)

import csv

with open('prices.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)

2. Xử lý ngoại lệ (try-except)

Xử lý ngoại lệ trong Python

Khi làm việc với file hoặc API, rất dễ gặp lỗi (file không tồn tại, mất kết nối...). Hãy luôn dùng try-except để chương trình không bị dừng đột ngột.

try:
with open('data.txt', 'r') as f:
content = f.read()
except FileNotFoundError:
print("Không tìm thấy file!")
except Exception as e:
print("Lỗi khác:", e)

3. Giới thiệu về API tài chính

Lấy dữ liệu từ API tài chính

  • API tài chính là dịch vụ cho phép bạn lấy dữ liệu giá, tin tức, chỉ số... từ các nguồn như Yahoo Finance, Alpha Vantage, Finnhub, Binance, v.v.
  • Dữ liệu thường trả về dạng JSON hoặc CSV.
  • Bạn sẽ dùng thư viện requests để gửi HTTP request và nhận dữ liệu.

4. Bài tập thực hành: Lấy dữ liệu từ API tài chính

a. Lấy giá cổ phiếu từ API miễn phí (ví dụ: Finnhub)

import requests

symbol = "AAPL"
api_key = "YOUR_API_KEY" # Đăng ký miễn phí tại https://finnhub.io/
url = f"https://finnhub.io/api/v1/quote?symbol={symbol}&token={api_key}"

try:
response = requests.get(url)
data = response.json()
print(f"Giá hiện tại của {symbol}: {data['c']}")
except Exception as e:
print("Lỗi khi lấy dữ liệu:", e)

b. Ghi dữ liệu ra file

with open('aapl_price.txt', 'w') as f:
f.write(str(data))

5. Gợi ý bài tập tự luyện

  1. Viết chương trình đọc file CSV chứa dữ liệu giá và tính giá trung bình.
  2. Viết chương trình lấy giá Bitcoin từ API Binance và lưu ra file.
  3. Thử cố tình nhập sai tên file để xem chương trình xử lý ngoại lệ ra sao.

6. Kết luận

Biết cách làm việc với file và API là nền tảng để phát triển các ứng dụng tài chính, bot trading, dashboard... Hãy luyện tập nhiều để thành thạo các thao tác này!


Tài liệu tham khảo

  1. Python File Handling
  2. Python requests library
  3. Finnhub API
  4. Binance API