Bài viết gần đây

| Tính toán chiến lược Buy/Sell Bot Auto Trading với DataFrame

Được viết bởi thanhdt vào ngày 16/11/2025 lúc 11:23 | 91 lượt xem

Tính toán chiến lược Buy/Sell Bot Auto Trading với DataFrame

Xây dựng bot giao dịch tự động đòi hỏi khả năng tính toán chính xác các tín hiệu mua/bán dựa trên dữ liệu thị trường. Với DataFrame trong Pandas, việc xử lý dữ liệu giá, tính toán chỉ báo kỹ thuật và tạo tín hiệu giao dịch trở nên đơn giản và hiệu quả. Bài viết này sẽ hướng dẫn bạn cách xây dựng một hệ thống tính toán chiến lược Buy/Sell hoàn chỉnh.

Tổng quan về Bot Auto Trading

Bot giao dịch tự động là chương trình máy tính thực hiện giao dịch dựa trên các quy tắc được lập trình sẵn. Các thành phần chính của một bot trading bao gồm:

  • Thu thập dữ liệu: Lấy dữ liệu giá từ sàn giao dịch
  • Tính toán chỉ báo: Tính các chỉ báo kỹ thuật (MA, RSI, MACD, v.v.)
  • Tạo tín hiệu: Xác định điểm mua (Buy) và bán (Sell)
  • Quản lý rủi ro: Đặt stop loss, take profit
  • Thực thi lệnh: Gửi lệnh mua/bán tự động

Cài đặt thư viện cần thiết

import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import warnings
warnings.filterwarnings('ignore')

# Thiết lập hiển thị
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)

Bước 1: Lấy và chuẩn bị dữ liệu

Lấy dữ liệu giá từ API

def get_price_data(symbol, period='1y', interval='1d'):
    """
    Lấy dữ liệu giá từ Yahoo Finance

    Parameters:
    symbol: Mã cổ phiếu/crypto (ví dụ: 'BTC-USD', 'AAPL')
    period: Khoảng thời gian ('1d', '5d', '1mo', '3mo', '6mo', '1y', '2y', '5y', '10y', 'ytd', 'max')
    interval: Khung thời gian ('1m', '2m', '5m', '15m', '30m', '60m', '90m', '1h', '1d', '5d', '1wk', '1mo', '3mo')
    """
    ticker = yf.Ticker(symbol)
    data = ticker.history(period=period, interval=interval)

    # Đảm bảo có đủ các cột cần thiết
    required_columns = ['Open', 'High', 'Low', 'Close', 'Volume']
    for col in required_columns:
        if col not in data.columns:
            raise ValueError(f"Thiếu cột {col} trong dữ liệu")

    # Đổi tên cột sang chữ thường để dễ làm việc
    data.columns = [col.lower() for col in data.columns]

    return data

# Lấy dữ liệu Bitcoin
df = get_price_data('BTC-USD', period='6mo', interval='1d')
print(f"Kích thước dữ liệu: {df.shape}")
print(df.head())

Làm sạch và chuẩn bị dữ liệu

def prepare_data(df):
    """
    Chuẩn bị và làm sạch dữ liệu
    """
    # Tạo bản sao để không ảnh hưởng dữ liệu gốc
    data = df.copy()

    # Xóa dữ liệu trùng lặp
    data = data.drop_duplicates()

    # Sắp xếp theo thời gian
    data = data.sort_index()

    # Xử lý giá trị thiếu
    data = data.fillna(method='ffill')  # Điền bằng giá trị trước đó

    # Đảm bảo giá trị hợp lệ (giá > 0)
    data = data[data['close'] > 0]
    data = data[data['volume'] >= 0]

    # Tính thêm các giá trị hữu ích
    data['returns'] = data['close'].pct_change()
    data['price_change'] = data['close'].diff()
    data['high_low_ratio'] = data['high'] / data['low']

    return data

# Chuẩn bị dữ liệu
df = prepare_data(df)
print(df.head())

Bước 2: Tính toán các chỉ báo kỹ thuật

Moving Average (Đường trung bình động)

def calculate_ma(data, period=20, ma_type='SMA'):
    """
    Tính đường trung bình động

    Parameters:
    data: Series hoặc DataFrame column
    period: Chu kỳ
    ma_type: Loại MA ('SMA', 'EMA', 'WMA')
    """
    if ma_type == 'SMA':
        return data.rolling(window=period).mean()
    elif ma_type == 'EMA':
        return data.ewm(span=period, adjust=False).mean()
    elif ma_type == 'WMA':
        weights = np.arange(1, period + 1)
        return data.rolling(window=period).apply(lambda x: np.dot(x, weights) / weights.sum(), raw=True)
    else:
        raise ValueError(f"Loại MA không hợp lệ: {ma_type}")

# Tính các đường MA
df['SMA_20'] = calculate_ma(df['close'], 20, 'SMA')
df['SMA_50'] = calculate_ma(df['close'], 50, 'SMA')
df['EMA_12'] = calculate_ma(df['close'], 12, 'EMA')
df['EMA_26'] = calculate_ma(df['close'], 26, 'EMA')

RSI (Relative Strength Index)

def calculate_rsi(data, period=14):
    """
    Tính chỉ báo RSI
    """
    delta = data.diff()
    gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()

    rs = gain / loss
    rsi = 100 - (100 / (1 + rs))

    return rsi

# Tính RSI
df['RSI'] = calculate_rsi(df['close'], 14)

MACD (Moving Average Convergence Divergence)

def calculate_macd(data, fast=12, slow=26, signal=9):
    """
    Tính chỉ báo MACD
    """
    ema_fast = calculate_ma(data, fast, 'EMA')
    ema_slow = calculate_ma(data, slow, 'EMA')

    macd_line = ema_fast - ema_slow
    signal_line = calculate_ma(macd_line, signal, 'EMA')
    histogram = macd_line - signal_line

    return macd_line, signal_line, histogram

# Tính MACD
df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = calculate_macd(df['close'])

Bollinger Bands

def calculate_bollinger_bands(data, period=20, std_dev=2):
    """
    Tính Bollinger Bands
    """
    sma = calculate_ma(data, period, 'SMA')
    std = data.rolling(window=period).std()

    upper_band = sma + (std * std_dev)
    lower_band = sma - (std * std_dev)

    return upper_band, lower_band, sma

# Tính Bollinger Bands
df['BB_Upper'], df['BB_Lower'], df['BB_Middle'] = calculate_bollinger_bands(df['close'])

Bước 3: Xây dựng chiến lược Buy/Sell

Chiến lược 1: Moving Average Crossover

Đây là chiến lược phổ biến nhất – mua khi MA ngắn hạn cắt lên trên MA dài hạn:

def ma_crossover_strategy(df, short_period=20, long_period=50):
    """
    Chiến lược Moving Average Crossover
    """
    data = df.copy()

    # Tính MA
    data['MA_Short'] = calculate_ma(data['close'], short_period, 'SMA')
    data['MA_Long'] = calculate_ma(data['close'], long_period, 'SMA')

    # Tạo tín hiệu
    # 1 = Buy, -1 = Sell, 0 = Hold
    data['Signal'] = 0

    # Tín hiệu mua: MA ngắn cắt lên trên MA dài
    data.loc[data['MA_Short'] > data['MA_Long'], 'Signal'] = 1

    # Tín hiệu bán: MA ngắn cắt xuống dưới MA dài
    data.loc[data['MA_Short'] < data['MA_Long'], 'Signal'] = -1

    # Tạo Position (chỉ thay đổi khi có tín hiệu mới)
    data['Position'] = data['Signal'].diff()

    # Chỉ giữ lại các điểm mua/bán thực sự
    data['Buy_Signal'] = (data['Position'] == 2).astype(int)  # Chuyển từ -1 sang 1
    data['Sell_Signal'] = (data['Position'] == -2).astype(int)  # Chuyển từ 1 sang -1

    return data

# Áp dụng chiến lược
df_strategy1 = ma_crossover_strategy(df, short_period=20, long_period=50)
print("Số tín hiệu MUA:", df_strategy1['Buy_Signal'].sum())
print("Số tín hiệu BÁN:", df_strategy1['Sell_Signal'].sum())

Chiến lược 2: RSI Overbought/Oversold

Mua khi RSI < 30 (oversold), bán khi RSI > 70 (overbought):

def rsi_strategy(df, rsi_period=14, oversold=30, overbought=70):
    """
    Chiến lược RSI Overbought/Oversold
    """
    data = df.copy()

    # Tính RSI
    data['RSI'] = calculate_rsi(data['close'], rsi_period)

    # Tạo tín hiệu
    data['Signal'] = 0

    # Mua khi RSI < oversold
    data.loc[data['RSI'] < oversold, 'Signal'] = 1

    # Bán khi RSI > overbought
    data.loc[data['RSI'] > overbought, 'Signal'] = -1

    # Tạo Position
    data['Position'] = data['Signal'].diff()
    data['Buy_Signal'] = (data['Position'] == 1).astype(int)
    data['Sell_Signal'] = (data['Position'] == -1).astype(int)

    return data

# Áp dụng chiến lược
df_strategy2 = rsi_strategy(df, rsi_period=14, oversold=30, overbought=70)
print("Số tín hiệu MUA:", df_strategy2['Buy_Signal'].sum())
print("Số tín hiệu BÁN:", df_strategy2['Sell_Signal'].sum())

Chiến lược 3: MACD Crossover

Mua khi MACD cắt lên trên Signal line, bán khi MACD cắt xuống dưới Signal:

def macd_strategy(df, fast=12, slow=26, signal=9):
    """
    Chiến lược MACD Crossover
    """
    data = df.copy()

    # Tính MACD
    data['MACD'], data['MACD_Signal'], data['MACD_Hist'] = calculate_macd(
        data['close'], fast, slow, signal
    )

    # Tạo tín hiệu
    data['Signal'] = 0

    # Mua khi MACD cắt lên trên Signal
    data.loc[data['MACD'] > data['MACD_Signal'], 'Signal'] = 1

    # Bán khi MACD cắt xuống dưới Signal
    data.loc[data['MACD'] < data['MACD_Signal'], 'Signal'] = -1

    # Tạo Position
    data['Position'] = data['Signal'].diff()
    data['Buy_Signal'] = (data['Position'] == 2).astype(int)
    data['Sell_Signal'] = (data['Position'] == -2).astype(int)

    return data

# Áp dụng chiến lược
df_strategy3 = macd_strategy(df)
print("Số tín hiệu MUA:", df_strategy3['Buy_Signal'].sum())
print("Số tín hiệu BÁN:", df_strategy3['Sell_Signal'].sum())

Chiến lược 4: Bollinger Bands

Mua khi giá chạm lower band, bán khi giá chạm upper band:

def bollinger_bands_strategy(df, period=20, std_dev=2):
    """
    Chiến lược Bollinger Bands
    """
    data = df.copy()

    # Tính Bollinger Bands
    data['BB_Upper'], data['BB_Lower'], data['BB_Middle'] = calculate_bollinger_bands(
        data['close'], period, std_dev
    )

    # Tạo tín hiệu
    data['Signal'] = 0

    # Mua khi giá chạm hoặc vượt qua lower band
    data.loc[data['close'] <= data['BB_Lower'], 'Signal'] = 1

    # Bán khi giá chạm hoặc vượt qua upper band
    data.loc[data['close'] >= data['BB_Upper'], 'Signal'] = -1

    # Tạo Position
    data['Position'] = data['Signal'].diff()
    data['Buy_Signal'] = (data['Position'] == 1).astype(int)
    data['Sell_Signal'] = (data['Position'] == -1).astype(int)

    return data

# Áp dụng chiến lược
df_strategy4 = bollinger_bands_strategy(df)
print("Số tín hiệu MUA:", df_strategy4['Buy_Signal'].sum())
print("Số tín hiệu BÁN:", df_strategy4['Sell_Signal'].sum())

Chiến lược 5: Kết hợp nhiều chỉ báo (Multi-Indicator Strategy)

Kết hợp nhiều chỉ báo để tăng độ chính xác:

def multi_indicator_strategy(df, 
                            ma_short=20, ma_long=50,
                            rsi_period=14, rsi_oversold=30, rsi_overbought=70,
                            macd_fast=12, macd_slow=26, macd_signal=9):
    """
    Chiến lược kết hợp nhiều chỉ báo
    Mua khi: MA Short > MA Long VÀ RSI < Oversold VÀ MACD > Signal
    Bán khi: MA Short < MA Long VÀ RSI > Overbought VÀ MACD < Signal
    """
    data = df.copy()

    # Tính các chỉ báo
    data['MA_Short'] = calculate_ma(data['close'], ma_short, 'SMA')
    data['MA_Long'] = calculate_ma(data['close'], ma_long, 'SMA')
    data['RSI'] = calculate_rsi(data['close'], rsi_period)
    data['MACD'], data['MACD_Signal'], _ = calculate_macd(
        data['close'], macd_fast, macd_slow, macd_signal
    )

    # Điều kiện mua: Tất cả điều kiện phải thỏa mãn
    buy_condition = (
        (data['MA_Short'] > data['MA_Long']) &
        (data['RSI'] < rsi_oversold) &
        (data['MACD'] > data['MACD_Signal'])
    )

    # Điều kiện bán: Tất cả điều kiện phải thỏa mãn
    sell_condition = (
        (data['MA_Short'] < data['MA_Long']) &
        (data['RSI'] > rsi_overbought) &
        (data['MACD'] < data['MACD_Signal'])
    )

    # Tạo tín hiệu
    data['Signal'] = 0
    data.loc[buy_condition, 'Signal'] = 1
    data.loc[sell_condition, 'Signal'] = -1

    # Tạo Position
    data['Position'] = data['Signal'].diff()
    data['Buy_Signal'] = (data['Position'] == 1).astype(int)
    data['Sell_Signal'] = (data['Position'] == -1).astype(int)

    return data

# Áp dụng chiến lược
df_strategy5 = multi_indicator_strategy(df)
print("Số tín hiệu MUA:", df_strategy5['Buy_Signal'].sum())
print("Số tín hiệu BÁN:", df_strategy5['Sell_Signal'].sum())

Bước 4: Thêm Stop Loss và Take Profit

Quản lý rủi ro là phần quan trọng trong trading:

def add_stop_loss_take_profit(df, stop_loss_pct=0.02, take_profit_pct=0.05):
    """
    Thêm Stop Loss và Take Profit vào chiến lược

    Parameters:
    stop_loss_pct: Phần trăm stop loss (ví dụ: 0.02 = 2%)
    take_profit_pct: Phần trăm take profit (ví dụ: 0.05 = 5%)
    """
    data = df.copy()

    # Khởi tạo các cột
    data['Entry_Price'] = np.nan
    data['Stop_Loss'] = np.nan
    data['Take_Profit'] = np.nan
    data['Exit_Signal'] = 0
    data['Position_Status'] = 0  # 0: Không có vị thế, 1: Đang nắm giữ

    entry_price = None
    position = 0  # 0: Không có, 1: Đang nắm giữ

    for i in range(len(data)):
        # Nếu có tín hiệu mua và chưa có vị thế
        if data['Buy_Signal'].iloc[i] == 1 and position == 0:
            entry_price = data['close'].iloc[i]
            data.loc[data.index[i], 'Entry_Price'] = entry_price
            data.loc[data.index[i], 'Stop_Loss'] = entry_price * (1 - stop_loss_pct)
            data.loc[data.index[i], 'Take_Profit'] = entry_price * (1 + take_profit_pct)
            position = 1
            data.loc[data.index[i], 'Position_Status'] = 1

        # Nếu đang có vị thế, kiểm tra stop loss và take profit
        elif position == 1 and entry_price is not None:
            current_price = data['close'].iloc[i]

            # Kiểm tra Stop Loss
            if current_price <= entry_price * (1 - stop_loss_pct):
                data.loc[data.index[i], 'Exit_Signal'] = -1
                position = 0
                entry_price = None

            # Kiểm tra Take Profit
            elif current_price >= entry_price * (1 + take_profit_pct):
                data.loc[data.index[i], 'Exit_Signal'] = 1
                position = 0
                entry_price = None

            # Kiểm tra tín hiệu bán thông thường
            elif data['Sell_Signal'].iloc[i] == 1:
                data.loc[data.index[i], 'Exit_Signal'] = -1
                position = 0
                entry_price = None
            else:
                data.loc[data.index[i], 'Position_Status'] = 1

        # Cập nhật trạng thái
        if position == 0:
            data.loc[data.index[i], 'Position_Status'] = 0

    return data

# Thêm Stop Loss và Take Profit
df_with_sl_tp = add_stop_loss_take_profit(df_strategy1, stop_loss_pct=0.02, take_profit_pct=0.05)
print("Số lần chạm Stop Loss:", (df_with_sl_tp['Exit_Signal'] == -1).sum())
print("Số lần chạm Take Profit:", (df_with_sl_tp['Exit_Signal'] == 1).sum())

Bước 5: Backtesting chiến lược

Backtesting giúp đánh giá hiệu quả của chiến lược trên dữ liệu lịch sử:

def backtest_strategy(df, initial_capital=10000, commission=0.001):
    """
    Backtest chiến lược giao dịch

    Parameters:
    initial_capital: Vốn ban đầu
    commission: Phí giao dịch (ví dụ: 0.001 = 0.1%)
    """
    data = df.copy()

    capital = initial_capital
    position = 0  # Số lượng coin/cổ phiếu đang nắm giữ
    trades = []
    equity_curve = [initial_capital]

    for i in range(len(data)):
        current_price = data['close'].iloc[i]

        # Tín hiệu mua
        if data['Buy_Signal'].iloc[i] == 1 and position == 0:
            # Tính số lượng có thể mua (trừ phí)
            available_capital = capital * (1 - commission)
            position = available_capital / current_price
            capital = 0

            trades.append({
                'date': data.index[i],
                'action': 'BUY',
                'price': current_price,
                'shares': position,
                'capital_used': available_capital
            })

        # Tín hiệu bán hoặc Exit Signal
        elif (data['Sell_Signal'].iloc[i] == 1 or data['Exit_Signal'].iloc[i] != 0) and position > 0:
            # Tính vốn sau khi bán (trừ phí)
            capital = position * current_price * (1 - commission)

            trades.append({
                'date': data.index[i],
                'action': 'SELL',
                'price': current_price,
                'shares': position,
                'capital_received': capital
            })

            position = 0

        # Tính giá trị danh mục hiện tại
        if position > 0:
            portfolio_value = position * current_price
        else:
            portfolio_value = capital

        equity_curve.append(portfolio_value)

    # Tính toán kết quả
    final_capital = equity_curve[-1]
    total_return = ((final_capital - initial_capital) / initial_capital) * 100

    # Tính các chỉ số hiệu suất
    equity_series = pd.Series(equity_curve)
    returns = equity_series.pct_change().dropna()

    if len(returns) > 0:
        sharpe_ratio = returns.mean() / returns.std() * np.sqrt(252) if returns.std() > 0 else 0
        max_drawdown = ((equity_series / equity_series.expanding().max()) - 1).min() * 100
    else:
        sharpe_ratio = 0
        max_drawdown = 0

    results = {
        'initial_capital': initial_capital,
        'final_capital': final_capital,
        'total_return': total_return,
        'total_trades': len(trades),
        'sharpe_ratio': sharpe_ratio,
        'max_drawdown': max_drawdown,
        'trades': trades,
        'equity_curve': equity_curve
    }

    return results, data

# Backtest chiến lược
results, df_backtest = backtest_strategy(df_strategy1, initial_capital=10000, commission=0.001)

print("=" * 50)
print("KẾT QUẢ BACKTEST")
print("=" * 50)
print(f"Vốn ban đầu: ${results['initial_capital']:,.2f}")
print(f"Vốn cuối cùng: ${results['final_capital']:,.2f}")
print(f"Lợi nhuận: {results['total_return']:.2f}%")
print(f"Tổng số giao dịch: {results['total_trades']}")
print(f"Sharpe Ratio: {results['sharpe_ratio']:.2f}")
print(f"Max Drawdown: {results['max_drawdown']:.2f}%")

Bước 6: Trực quan hóa kết quả

def visualize_strategy(df, results, strategy_name="Strategy"):
    """
    Vẽ biểu đồ chiến lược và kết quả
    """
    fig, axes = plt.subplots(3, 1, figsize=(16, 12), sharex=True)

    # Biểu đồ 1: Giá và tín hiệu
    ax1 = axes[0]
    ax1.plot(df.index, df['close'], label='Giá đóng cửa', linewidth=2, color='black')

    # Vẽ các đường MA nếu có
    if 'MA_Short' in df.columns and 'MA_Long' in df.columns:
        ax1.plot(df.index, df['MA_Short'], label='MA Short', linewidth=1.5, alpha=0.7)
        ax1.plot(df.index, df['MA_Long'], label='MA Long', linewidth=1.5, alpha=0.7)

    # Đánh dấu điểm mua
    buy_signals = df[df['Buy_Signal'] == 1]
    ax1.scatter(buy_signals.index, buy_signals['close'], 
               marker='^', color='green', s=100, label='Tín hiệu MUA', zorder=5)

    # Đánh dấu điểm bán
    sell_signals = df[df['Sell_Signal'] == 1]
    ax1.scatter(sell_signals.index, sell_signals['close'], 
               marker='v', color='red', s=100, label='Tín hiệu BÁN', zorder=5)

    ax1.set_title(f'{strategy_name} - Giá và Tín hiệu', fontsize=14, fontweight='bold')
    ax1.set_ylabel('Giá')
    ax1.legend()
    ax1.grid(True, alpha=0.3)

    # Biểu đồ 2: Equity Curve
    ax2 = axes[1]
    equity_curve = pd.Series(results['equity_curve'], index=df.index[:len(results['equity_curve'])])
    ax2.plot(equity_curve.index, equity_curve.values, label='Equity Curve', linewidth=2, color='blue')
    ax2.axhline(y=results['initial_capital'], color='red', linestyle='--', label='Vốn ban đầu')
    ax2.set_title('Equity Curve', fontsize=14, fontweight='bold')
    ax2.set_ylabel('Giá trị danh mục ($)')
    ax2.legend()
    ax2.grid(True, alpha=0.3)

    # Biểu đồ 3: Drawdown
    ax3 = axes[2]
    equity_series = pd.Series(results['equity_curve'])
    running_max = equity_series.expanding().max()
    drawdown = ((equity_series / running_max) - 1) * 100
    ax3.fill_between(drawdown.index, drawdown.values, 0, alpha=0.3, color='red')
    ax3.plot(drawdown.index, drawdown.values, linewidth=1, color='red')
    ax3.set_title('Drawdown', fontsize=14, fontweight='bold')
    ax3.set_xlabel('Thời gian')
    ax3.set_ylabel('Drawdown (%)')
    ax3.grid(True, alpha=0.3)

    plt.tight_layout()
    plt.show()

# Vẽ biểu đồ
visualize_strategy(df_strategy1, results, "Moving Average Crossover")

Ví dụ hoàn chỉnh: Hệ thống Bot Trading

Dưới đây là một ví dụ hoàn chỉnh kết hợp tất cả các bước:

class TradingBot:
    """
    Lớp Bot Trading hoàn chỉnh
    """
    def __init__(self, symbol, initial_capital=10000, commission=0.001):
        self.symbol = symbol
        self.initial_capital = initial_capital
        self.commission = commission
        self.df = None
        self.results = None

    def load_data(self, period='6mo', interval='1d'):
        """Lấy và chuẩn bị dữ liệu"""
        self.df = get_price_data(self.symbol, period, interval)
        self.df = prepare_data(self.df)
        return self.df

    def calculate_indicators(self):
        """Tính toán các chỉ báo"""
        if self.df is None:
            raise ValueError("Chưa có dữ liệu. Hãy gọi load_data() trước.")

        # MA
        self.df['MA_Short'] = calculate_ma(self.df['close'], 20, 'SMA')
        self.df['MA_Long'] = calculate_ma(self.df['close'], 50, 'SMA')

        # RSI
        self.df['RSI'] = calculate_rsi(self.df['close'], 14)

        # MACD
        self.df['MACD'], self.df['MACD_Signal'], _ = calculate_macd(self.df['close'])

        return self.df

    def generate_signals(self, strategy='ma_crossover'):
        """Tạo tín hiệu mua/bán"""
        if self.df is None:
            raise ValueError("Chưa có dữ liệu.")

        if strategy == 'ma_crossover':
            self.df = ma_crossover_strategy(self.df)
        elif strategy == 'rsi':
            self.df = rsi_strategy(self.df)
        elif strategy == 'macd':
            self.df = macd_strategy(self.df)
        elif strategy == 'multi':
            self.df = multi_indicator_strategy(self.df)
        else:
            raise ValueError(f"Chiến lược không hợp lệ: {strategy}")

        # Thêm Stop Loss và Take Profit
        self.df = add_stop_loss_take_profit(self.df)

        return self.df

    def backtest(self):
        """Backtest chiến lược"""
        if self.df is None:
            raise ValueError("Chưa có dữ liệu.")

        self.results, self.df = backtest_strategy(
            self.df, 
            self.initial_capital, 
            self.commission
        )

        return self.results

    def print_results(self):
        """In kết quả backtest"""
        if self.results is None:
            print("Chưa có kết quả backtest.")
            return

        print("=" * 60)
        print("KẾT QUẢ BACKTEST")
        print("=" * 60)
        print(f"Symbol: {self.symbol}")
        print(f"Vốn ban đầu: ${self.results['initial_capital']:,.2f}")
        print(f"Vốn cuối cùng: ${self.results['final_capital']:,.2f}")
        print(f"Lợi nhuận: {self.results['total_return']:.2f}%")
        print(f"Tổng số giao dịch: {self.results['total_trades']}")
        print(f"Sharpe Ratio: {self.results['sharpe_ratio']:.2f}")
        print(f"Max Drawdown: {self.results['max_drawdown']:.2f}%")
        print("=" * 60)

    def visualize(self, strategy_name="Strategy"):
        """Vẽ biểu đồ"""
        if self.results is None:
            print("Chưa có kết quả backtest.")
            return

        visualize_strategy(self.df, self.results, strategy_name)

# Sử dụng Bot Trading
bot = TradingBot('BTC-USD', initial_capital=10000, commission=0.001)

# Load dữ liệu
bot.load_data(period='6mo', interval='1d')

# Tính chỉ báo
bot.calculate_indicators()

# Tạo tín hiệu với chiến lược MA Crossover
bot.generate_signals(strategy='ma_crossover')

# Backtest
bot.backtest()

# In kết quả
bot.print_results()

# Vẽ biểu đồ
bot.visualize("Moving Average Crossover Strategy")

Kết luận

Tính toán chiến lược Buy/Sell với DataFrame trong Python là nền tảng quan trọng để xây dựng bot giao dịch tự động. Với Pandas, bạn có thể:

  • Xử lý và chuẩn bị dữ liệu giá hiệu quả
  • Tính toán các chỉ báo kỹ thuật phức tạp
  • Xây dựng nhiều chiến lược giao dịch khác nhau
  • Thêm quản lý rủi ro với Stop Loss và Take Profit
  • Backtest và đánh giá hiệu quả chiến lược
  • Trực quan hóa kết quả

Để tìm hiểu thêm về xử lý dữ liệu với DataFrame, bạn có thể xem bài viết Giới thiệu về Pandas hoặc Thống kê lượng với DataFrame. Nếu bạn muốn tìm hiểu về các chỉ báo kỹ thuật, hãy tham khảo bài viết Phân tích kỹ thuật với Python.

Tài liệu tham khảo