Bot Python Chứng KhoánPortfolio BotPythonRebalancingQuản lý danh mục
Được viết bởithanhdtvào ngày 06/07/2026

Bot Quản Lý Danh Mục Đầu Tư Tự Động Với Python

Xây dựng bot Python tự động rebalance danh mục đầu tư crypto/chứng khoán theo tỷ lệ mục tiêu, tính toán hiệu suất và gửi báo cáo Telegram hàng ngày.

Bot Quản Lý Danh Mục Đầu Tư Tự Động Với Python

Rebalancing danh mục đầu tư là một trong những chiến lược quản lý vốn đã được chứng minh qua nhiều thập kỷ. Bot Python có thể tự động hóa toàn bộ quy trình này — từ tính toán tỷ trọng, thực hiện giao dịch đến báo cáo hiệu suất.

Tại Sao Cần Rebalancing Tự Động?

Giả sử danh mục mục tiêu của bạn: 60% BTC, 30% ETH, 10% USDT.

Sau 3 tháng BTC tăng mạnh, tỷ trọng tự nhiên thay đổi: 75% BTC, 20% ETH, 5% USDT. Danh mục bây giờ quá tập trung vào BTC — rủi ro tăng cao.

Rebalancing = bán bớt BTC, mua thêm ETH/USDT để về đúng 60/30/10. Bot làm điều này tự động, kỷ luật, không cảm xúc.

Cấu Trúc Bot Portfolio

from dataclasses import dataclass
from typing import Dict
import ccxt
import pandas as pd
from datetime import datetime

@dataclass
class PortfolioConfig:
    target_weights: Dict[str, float]  # {"BTC/USDT": 0.6, "ETH/USDT": 0.3}
    rebalance_threshold: float = 0.05  # Rebalance khi lệch > 5%
    min_trade_value: float = 10.0      # Không trade nếu < $10

class PortfolioBot:
    def __init__(self, exchange: ccxt.Exchange, config: PortfolioConfig):
        self.exchange = exchange
        self.config = config

    def get_portfolio_value(self) -> Dict[str, float]:
        """Lấy giá trị hiện tại của danh mục"""
        balance = self.exchange.fetch_balance()
        tickers = self.exchange.fetch_tickers(list(self.config.target_weights.keys()))

        portfolio = {}
        for symbol, weight in self.config.target_weights.items():
            coin = symbol.split('/')[0]
            qty = balance.get(coin, {}).get('total', 0)
            price = tickers[symbol]['last']
            portfolio[symbol] = qty * price

        return portfolio

    def calculate_rebalance_orders(self) -> list:
        """Tính toán các lệnh cần thiết để rebalance"""
        portfolio = self.get_portfolio_value()
        total_value = sum(portfolio.values())

        orders = []
        for symbol, target_weight in self.config.target_weights.items():
            current_value = portfolio.get(symbol, 0)
            current_weight = current_value / total_value
            target_value = total_value * target_weight

            diff = target_value - current_value
            diff_weight = abs(current_weight - target_weight)

            # Chỉ rebalance nếu lệch đủ nhiều
            if (diff_weight > self.config.rebalance_threshold and
                    abs(diff) > self.config.min_trade_value):
                orders.append({
                    'symbol': symbol,
                    'side': 'buy' if diff > 0 else 'sell',
                    'value': abs(diff),
                    'current_weight': f"{current_weight*100:.1f}%",
                    'target_weight': f"{target_weight*100:.1f}%"
                })

        return orders

    def execute_rebalance(self):
        """Thực hiện rebalance"""
        orders = self.calculate_rebalance_orders()

        if not orders:
            print(f"{datetime.now()}: Portfolio cân bằng, không cần rebalance")
            return

        for order in orders:
            ticker = self.exchange.fetch_ticker(order['symbol'])
            price = ticker['last']
            quantity = order['value'] / price

            print(f"Rebalancing: {order['side'].upper()} {order['symbol']} "
                  f"${order['value']:.2f} | "
                  f"{order['current_weight']} → {order['target_weight']}")

            self.exchange.create_market_order(
                order['symbol'],
                order['side'],
                round(quantity, 6)
            )

Tính Hiệu Suất Danh Mục

def calculate_performance(portfolio_history: pd.DataFrame) -> dict:
    """
    portfolio_history: DataFrame với index=date, column='value'
    """
    daily_returns = portfolio_history['value'].pct_change().dropna()

    total_return = (portfolio_history['value'].iloc[-1] /
                    portfolio_history['value'].iloc[0] - 1) * 100

    # Sharpe Ratio (giả sử risk-free rate = 4%/năm)
    rf_daily = 0.04 / 252
    sharpe = (daily_returns.mean() - rf_daily) / daily_returns.std() * (252 ** 0.5)

    # Max Drawdown
    rolling_max = portfolio_history['value'].cummax()
    drawdown = (portfolio_history['value'] - rolling_max) / rolling_max
    max_drawdown = drawdown.min() * 100

    return {
        'total_return': f"{total_return:.2f}%",
        'sharpe_ratio': f"{sharpe:.2f}",
        'max_drawdown': f"{max_drawdown:.2f}%",
        'volatility': f"{daily_returns.std() * (252**0.5) * 100:.2f}%"
    }

Gửi Báo Cáo Telegram Hàng Ngày

import requests

def send_telegram_report(bot_token: str, chat_id: str, report: dict):
    portfolio = report['portfolio']
    perf = report['performance']

    message = f"""
📊 *Báo Cáo Danh Mục Hôm Nay*
📅 {datetime.now().strftime('%d/%m/%Y %H:%M')}

💼 *Tổng giá trị:* ${report['total_value']:,.2f}

*Phân bổ hiện tại:*
"""
    for symbol, data in portfolio.items():
        message += f"• {symbol}: ${data['value']:,.2f} ({data['weight']:.1f}%)\n"

    message += f"""
📈 *Hiệu suất:*
• Tổng lợi nhuận: {perf['total_return']}
• Sharpe Ratio: {perf['sharpe_ratio']}
• Max Drawdown: {perf['max_drawdown']}
"""

    requests.post(
        f"https://api.telegram.org/bot{bot_token}/sendMessage",
        json={"chat_id": chat_id, "text": message, "parse_mode": "Markdown"}
    )

Lên Lịch Chạy Tự Động

import schedule

bot = PortfolioBot(exchange, PortfolioConfig(
    target_weights={"BTC/USDT": 0.6, "ETH/USDT": 0.3, "BNB/USDT": 0.1}
))

# Rebalance mỗi tuần vào thứ Hai lúc 8:00
schedule.every().monday.at("08:00").do(bot.execute_rebalance)

# Báo cáo hàng ngày lúc 20:00
schedule.every().day.at("20:00").do(lambda: send_telegram_report(...))

while True:
    schedule.run_pending()
    time.sleep(60)

Xây dựng hệ thống quản lý danh mục đầu tư tự động hoàn chỉnh tại Hướng Nghiệp Dữ Liệu.