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

| Chiến lược Range-Bound Trading (Sideway)

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

Chiến lược Range-Bound Trading (Sideway): Hướng dẫn Python

Range-Bound Trading (hay còn gọi là Sideway Trading) là một chiến lược giao dịch hiệu quả khi thị trường không có xu hướng rõ ràng và giá dao động trong một vùng nhất định. Trong bài viết này, chúng ta sẽ tìm hiểu các chiến lược Range-Bound Trading thực sự hiệu quả và cách triển khai chúng bằng Python.

1. Hiểu về Range-Bound Trading

Range-Bound Trading là chiến lược giao dịch dựa trên giả định rằng giá sẽ tiếp tục dao động giữa mức hỗ trợ (support) và kháng cự (resistance) trong một khoảng thời gian. Khác với trend trading, range trading tận dụng sự dao động giá trong một vùng.

Đặc điểm của thị trường Range-Bound:

  • Giá dao động giữa Support và Resistance: Giá liên tục test và bật lại từ các mức này
  • Không có xu hướng rõ ràng: Giá không tạo higher highs/lower lows
  • Volume thấp: Thường có volume thấp hơn so với thị trường có xu hướng
  • Phù hợp với các cặp tiền tệ: Đặc biệt hiệu quả với các cặp tiền tệ chính

Công thức xác định Range:

import pandas as pd
import numpy as np

def identify_range(df, period=20):
    """
    Xác định vùng range (support và resistance)
    
    Parameters:
    -----------
    df : pd.DataFrame
        DataFrame chứa OHLCV data
    period : int
        Số nến để xác định range
        
    Returns:
    --------
    dict: Chứa support, resistance, và range width
    """
    # Lấy giá cao và thấp trong khoảng thời gian
    recent_highs = df['High'].rolling(window=period).max()
    recent_lows = df['Low'].rolling(window=period).min()
    
    # Xác định resistance (kháng cự) - giá cao nhất
    resistance = recent_highs.max()
    
    # Xác định support (hỗ trợ) - giá thấp nhất
    support = recent_lows.min()
    
    # Tính độ rộng của range
    range_width = resistance - support
    range_width_pct = (range_width / support) * 100
    
    return {
        'support': support,
        'resistance': resistance,
        'range_width': range_width,
        'range_width_pct': range_width_pct,
        'midpoint': (support + resistance) / 2
    }

2. Các chiến lược Range-Bound Trading hiệu quả

2.1. Chiến lược Support/Resistance Cơ bản

Đặc điểm:

  • Đơn giản, dễ triển khai
  • Mua ở support, bán ở resistance
  • Phù hợp với thị trường sideway rõ ràng

Quy tắc:

  • Mua: Giá chạm hoặc gần support và bắt đầu tăng
  • Bán: Giá chạm hoặc gần resistance và bắt đầu giảm
class BasicRangeStrategy:
    """Chiến lược Range-Bound cơ bản"""
    
    def __init__(self, period=20, support_buffer=0.001, resistance_buffer=0.001):
        """
        Parameters:
        -----------
        period : int
            Số nến để xác định range
        support_buffer : float
            Buffer % để xác định vùng mua (0.001 = 0.1%)
        resistance_buffer : float
            Buffer % để xác định vùng bán
        """
        self.period = period
        self.support_buffer = support_buffer
        self.resistance_buffer = resistance_buffer
    
    def identify_range(self, df):
        """Xác định support và resistance"""
        recent_highs = df['High'].tail(self.period)
        recent_lows = df['Low'].tail(self.period)
        
        resistance = recent_highs.max()
        support = recent_lows.min()
        
        return support, resistance
    
    def generate_signals(self, df):
        """
        Tạo tín hiệu giao dịch
        
        Returns:
        --------
        pd.Series: 1 = Mua, -1 = Bán, 0 = Giữ
        """
        df = df.copy()
        df['Signal'] = 0
        
        # Tính support và resistance cho mỗi nến
        for i in range(self.period, len(df)):
            window_df = df.iloc[i-self.period:i]
            support, resistance = self.identify_range(window_df)
            
            current_price = df.iloc[i]['Close']
            prev_price = df.iloc[i-1]['Close']
            
            # Vùng mua: giá gần support
            support_zone = support * (1 + self.support_buffer)
            if current_price <= support_zone and prev_price > current_price:
                df.iloc[i, df.columns.get_loc('Signal')] = 1
            
            # Vùng bán: giá gần resistance
            resistance_zone = resistance * (1 - self.resistance_buffer)
            if current_price >= resistance_zone and prev_price < current_price:
                df.iloc[i, df.columns.get_loc('Signal')] = -1
        
        return df['Signal']

2.2. Chiến lược Bollinger Bands trong Range (Hiệu quả cao)

Đặc điểm:

  • Sử dụng Bollinger Bands để xác định vùng range
  • Mua khi giá chạm dải dưới, bán khi chạm dải trên
  • Giảm false signals đáng kể

Quy tắc:

  • Mua: Giá chạm dải dưới Bollinger Bands trong vùng range
  • Bán: Giá chạm dải trên Bollinger Bands trong vùng range
import pandas_ta as ta

class BollingerBandsRangeStrategy:
    """Chiến lược Range-Bound với Bollinger Bands"""
    
    def __init__(self, bb_period=20, bb_std=2.0, range_period=50):
        """
        Parameters:
        -----------
        bb_period : int
            Period cho Bollinger Bands
        bb_std : float
            Độ lệch chuẩn cho Bollinger Bands
        range_period : int
            Period để xác định thị trường có phải range không
        """
        self.bb_period = bb_period
        self.bb_std = bb_std
        self.range_period = range_period
    
    def is_range_market(self, df):
        """
        Kiểm tra xem thị trường có phải range không
        Sử dụng ADX (Average Directional Index) - ADX < 25 = range
        """
        if len(df) < self.range_period + 14:
            return False
        
        # Tính ADX
        adx = ta.adx(df['High'], df['Low'], df['Close'], length=14)
        
        if adx is None or len(adx) == 0:
            return False
        
        # Lấy giá trị ADX cuối cùng
        current_adx = adx.iloc[-1, 0] if isinstance(adx, pd.DataFrame) else adx.iloc[-1]
        
        # ADX < 25 thường được coi là thị trường range
        return current_adx < 25
    
    def generate_signals(self, df):
        """Tạo tín hiệu giao dịch"""
        df = df.copy()
        
        # Kiểm tra xem có phải range market không
        if not self.is_range_market(df):
            return pd.Series(0, index=df.index)
        
        # Tính Bollinger Bands
        bb = ta.bbands(df['Close'], length=self.bb_period, std=self.bb_std)
        
        if bb is None:
            return pd.Series(0, index=df.index)
        
        df['BB_Upper'] = bb.iloc[:, 0]  # BBU
        df['BB_Middle'] = bb.iloc[:, 1]  # BBM
        df['BB_Lower'] = bb.iloc[:, 2]  # BBL
        
        df['Signal'] = 0
        
        # Tín hiệu mua: Giá chạm hoặc dưới dải dưới
        buy_condition = (
            (df['Close'] <= df['BB_Lower']) |
            ((df['Close'] <= df['BB_Lower'] * 1.001) & 
             (df['Close'].shift(1) > df['BB_Lower'].shift(1)))
        )
        df.loc[buy_condition, 'Signal'] = 1
        
        # Tín hiệu bán: Giá chạm hoặc trên dải trên
        sell_condition = (
            (df['Close'] >= df['BB_Upper']) |
            ((df['Close'] >= df['BB_Upper'] * 0.999) & 
             (df['Close'].shift(1) < df['BB_Upper'].shift(1)))
        )
        df.loc[sell_condition, 'Signal'] = -1
        
        return df['Signal']

2.3. Chiến lược RSI trong Range (Nâng cao – Rất hiệu quả)

Đặc điểm:

  • Kết hợp RSI với range trading
  • Mua khi RSI oversold trong range, bán khi RSI overbought
  • Tín hiệu mạnh, độ chính xác cao

Quy tắc:

  • Mua: RSI < 30 (oversold) và giá gần support
  • Bán: RSI > 70 (overbought) và giá gần resistance
class RSIRangeStrategy:
    """Chiến lược Range-Bound với RSI"""
    
    def __init__(self, rsi_period=14, range_period=20, 
                 oversold=30, overbought=70):
        """
        Parameters:
        -----------
        rsi_period : int
            Period cho RSI
        range_period : int
            Period để xác định range
        oversold : float
            Ngưỡng oversold (mặc định 30)
        overbought : float
            Ngưỡng overbought (mặc định 70)
        """
        self.rsi_period = rsi_period
        self.range_period = range_period
        self.oversold = oversold
        self.overbought = overbought
    
    def calculate_rsi(self, prices):
        """Tính RSI"""
        delta = prices.diff()
        gain = (delta.where(delta > 0, 0)).rolling(window=self.rsi_period).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(window=self.rsi_period).mean()
        rs = gain / loss
        rsi = 100 - (100 / (1 + rs))
        return rsi
    
    def identify_range(self, df):
        """Xác định support và resistance"""
        recent_highs = df['High'].tail(self.range_period)
        recent_lows = df['Low'].tail(self.range_period)
        
        resistance = recent_highs.max()
        support = recent_lows.min()
        
        return support, resistance
    
    def generate_signals(self, df):
        """Tạo tín hiệu giao dịch"""
        df = df.copy()
        
        # Tính RSI
        df['RSI'] = self.calculate_rsi(df['Close'])
        
        df['Signal'] = 0
        
        for i in range(self.range_period, len(df)):
            window_df = df.iloc[i-self.range_period:i]
            support, resistance = self.identify_range(window_df)
            
            current_price = df.iloc[i]['Close']
            current_rsi = df.iloc[i]['RSI']
            
            # Tính vị trí giá trong range (0 = support, 1 = resistance)
            range_position = (current_price - support) / (resistance - support)
            
            # Tín hiệu mua: RSI oversold và giá gần support
            if (current_rsi < self.oversold and 
                range_position < 0.3):  # Trong 30% dưới của range
                df.iloc[i, df.columns.get_loc('Signal')] = 1
            
            # Tín hiệu bán: RSI overbought và giá gần resistance
            if (current_rsi > self.overbought and 
                range_position > 0.7):  # Trong 30% trên của range
                df.iloc[i, df.columns.get_loc('Signal')] = -1
        
        return df['Signal']

2.4. Chiến lược Stochastic Oscillator trong Range (Rất hiệu quả)

Đặc điểm:

  • Sử dụng Stochastic để xác định điểm vào lệnh
  • Phù hợp với thị trường range
  • Tín hiệu rõ ràng và dễ theo dõi

Quy tắc:

  • Mua: Stochastic < 20 (oversold) và giá trong vùng range
  • Bán: Stochastic > 80 (overbought) và giá trong vùng range
class StochasticRangeStrategy:
    """Chiến lược Range-Bound với Stochastic Oscillator"""
    
    def __init__(self, stoch_k=14, stoch_d=3, 
                 range_period=20, oversold=20, overbought=80):
        """
        Parameters:
        -----------
        stoch_k : int
            Period %K cho Stochastic
        stoch_d : int
            Period %D cho Stochastic
        range_period : int
            Period để xác định range
        oversold : float
            Ngưỡng oversold
        overbought : float
            Ngưỡng overbought
        """
        self.stoch_k = stoch_k
        self.stoch_d = stoch_d
        self.range_period = range_period
        self.oversold = oversold
        self.overbought = overbought
    
    def calculate_stochastic(self, df):
        """Tính Stochastic Oscillator"""
        stoch = ta.stoch(df['High'], df['Low'], df['Close'], 
                        k=self.stoch_k, d=self.stoch_d)
        
        if stoch is None:
            return None, None
        
        stoch_k = stoch.iloc[:, 0]  # %K
        stoch_d = stoch.iloc[:, 1]  # %D
        
        return stoch_k, stoch_d
    
    def identify_range(self, df):
        """Xác định support và resistance"""
        recent_highs = df['High'].tail(self.range_period)
        recent_lows = df['Low'].tail(self.range_period)
        
        resistance = recent_highs.max()
        support = recent_lows.min()
        
        return support, resistance
    
    def generate_signals(self, df):
        """Tạo tín hiệu giao dịch"""
        df = df.copy()
        
        # Tính Stochastic
        stoch_k, stoch_d = self.calculate_stochastic(df)
        
        if stoch_k is None:
            return pd.Series(0, index=df.index)
        
        df['Stoch_K'] = stoch_k
        df['Stoch_D'] = stoch_d
        
        df['Signal'] = 0
        
        for i in range(self.range_period, len(df)):
            window_df = df.iloc[i-self.range_period:i]
            support, resistance = self.identify_range(window_df)
            
            current_price = df.iloc[i]['Close']
            current_stoch_k = df.iloc[i]['Stoch_K']
            current_stoch_d = df.iloc[i]['Stoch_D']
            
            # Kiểm tra giá có trong range không
            if current_price < support or current_price > resistance:
                continue
            
            # Tín hiệu mua: Stochastic oversold và %K cắt lên %D
            if (current_stoch_k < self.oversold and 
                current_stoch_k > current_stoch_d and
                df.iloc[i-1]['Stoch_K'] <= df.iloc[i-1]['Stoch_D']):
                df.iloc[i, df.columns.get_loc('Signal')] = 1
            
            # Tín hiệu bán: Stochastic overbought và %K cắt xuống %D
            if (current_stoch_k > self.overbought and 
                current_stoch_k < current_stoch_d and
                df.iloc[i-1]['Stoch_K'] >= df.iloc[i-1]['Stoch_D']):
                df.iloc[i, df.columns.get_loc('Signal')] = -1
        
        return df['Signal']

3. Bot Auto Trading Range-Bound hoàn chỉnh

3.1. Bot với Quản lý Rủi ro và Position Management

import ccxt
import pandas as pd
import numpy as np
import time
from datetime import datetime
from typing import Dict, Optional

class RangeBoundTradingBot:
    """Bot auto trading sử dụng chiến lược Range-Bound"""
    
    def __init__(self, exchange_name: str, api_key: str, api_secret: str, 
                 strategy_type: str = 'rsi_range'):
        """
        Khởi tạo bot
        
        Parameters:
        -----------
        exchange_name : str
            Tên sàn (binance, coinbase, etc.)
        api_key : str
            API key
        api_secret : str
            API secret
        strategy_type : str
            Loại chiến lược ('basic', 'bb_range', 'rsi_range', 'stoch_range')
        """
        # Kết nối exchange
        exchange_class = getattr(ccxt, exchange_name)
        self.exchange = exchange_class({
            'apiKey': api_key,
            'secret': api_secret,
            'enableRateLimit': True,
        })
        
        # Chọn chiến lược
        self.strategy = self._init_strategy(strategy_type)
        
        # Quản lý vị thế
        self.position = None
        self.entry_price = None
        self.stop_loss = None
        self.take_profit = None
        
        # Cài đặt rủi ro
        self.max_position_size = 0.1  # 10% vốn
        self.stop_loss_pct = 0.02  # 2%
        self.take_profit_pct = 0.04  # 4%
    
    def _init_strategy(self, strategy_type: str):
        """Khởi tạo chiến lược"""
        if strategy_type == 'basic':
            return BasicRangeStrategy()
        elif strategy_type == 'bb_range':
            return BollingerBandsRangeStrategy()
        elif strategy_type == 'rsi_range':
            return RSIRangeStrategy()
        elif strategy_type == 'stoch_range':
            return StochasticRangeStrategy()
        else:
            raise ValueError(f"Unknown strategy type: {strategy_type}")
    
    def get_market_data(self, symbol: str, timeframe: str = '1h', limit: int = 100):
        """Lấy dữ liệu thị trường"""
        ohlcv = self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
        df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
        df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
        df.set_index('timestamp', inplace=True)
        df.columns = [col.capitalize() for col in df.columns]
        return df
    
    def calculate_position_size(self, balance: float, price: float) -> float:
        """Tính toán kích thước vị thế"""
        max_position_value = balance * self.max_position_size
        position_size = max_position_value / price
        return position_size
    
    def place_order(self, symbol: str, side: str, amount: float, 
                   order_type: str = 'market'):
        """Đặt lệnh giao dịch"""
        try:
            if side == 'buy':
                order = self.exchange.create_market_buy_order(symbol, amount)
            else:
                order = self.exchange.create_market_sell_order(symbol, amount)
            
            print(f"[{datetime.now()}] {side.upper()} {amount} {symbol} @ {order['price']}")
            return order
        except Exception as e:
            print(f"Error placing order: {e}")
            return None
    
    def check_stop_loss_take_profit(self, current_price: float):
        """Kiểm tra stop loss và take profit"""
        if self.position is None:
            return
        
        if self.position == 'long':
            # Kiểm tra stop loss
            if current_price <= self.stop_loss:
                print(f"[{datetime.now()}] Stop Loss triggered @ {current_price}")
                self.close_position(current_price)
                return
            
            # Kiểm tra take profit
            if current_price >= self.take_profit:
                print(f"[{datetime.now()}] Take Profit triggered @ {current_price}")
                self.close_position(current_price)
                return
    
    def open_position(self, symbol: str, side: str, price: float, amount: float):
        """Mở vị thế"""
        order = self.place_order(symbol, side, amount)
        if order:
            self.position = side
            self.entry_price = price
            
            # Đặt stop loss và take profit
            if side == 'long':
                self.stop_loss = price * (1 - self.stop_loss_pct)
                self.take_profit = price * (1 + self.take_profit_pct)
            
            print(f"[{datetime.now()}] Position opened: {side} @ {price}")
    
    def close_position(self, price: float):
        """Đóng vị thế"""
        if self.position:
            # Tính toán lợi nhuận
            if self.position == 'long':
                pnl_pct = ((price - self.entry_price) / self.entry_price) * 100
                print(f"[{datetime.now()}] Position closed. P&L: {pnl_pct:.2f}%")
            
            self.position = None
            self.entry_price = None
            self.stop_loss = None
            self.take_profit = None
    
    def run(self, symbol: str, timeframe: str = '1h', check_interval: int = 300):
        """
        Chạy bot
        
        Parameters:
        -----------
        symbol : str
            Trading pair
        timeframe : str
            Khung thời gian
        check_interval : int
            Thời gian chờ giữa các lần kiểm tra (giây)
        """
        print(f"[{datetime.now()}] Bot started for {symbol}")
        
        while True:
            try:
                # Lấy dữ liệu thị trường
                df = self.get_market_data(symbol, timeframe)
                current_price = df['Close'].iloc[-1]
                
                # Kiểm tra stop loss và take profit
                if self.position:
                    self.check_stop_loss_take_profit(current_price)
                    if self.position is None:  # Đã đóng vị thế
                        time.sleep(check_interval)
                        continue
                
                # Tạo tín hiệu
                signals = self.strategy.generate_signals(df)
                signal = signals.iloc[-1]
                
                # Xử lý tín hiệu
                if signal == 1 and self.position != 'long':
                    # Tín hiệu mua
                    balance = self.exchange.fetch_balance()
                    available_balance = balance['USDT']['free'] if 'USDT' in balance else balance['total']['USDT']
                    amount = self.calculate_position_size(available_balance, current_price)
                    
                    if amount > 0:
                        self.open_position(symbol, 'long', current_price, amount)
                
                elif signal == -1 and self.position == 'long':
                    # Tín hiệu bán
                    self.close_position(current_price)
                
                # Đợi đến lần kiểm tra tiếp theo
                time.sleep(check_interval)
                
            except KeyboardInterrupt:
                print(f"[{datetime.now()}] Bot stopped by user")
                break
            except Exception as e:
                print(f"[{datetime.now()}] Error: {e}")
                time.sleep(check_interval)

4. Backtesting Chiến lược Range-Bound

4.1. Hàm Backtest

def backtest_range_strategy(df, strategy, initial_capital=10000):
    """
    Backtest chiến lược Range-Bound
    
    Parameters:
    -----------
    df : pd.DataFrame
        Dữ liệu OHLCV
    strategy : Strategy object
        Đối tượng chiến lược
    initial_capital : float
        Vốn ban đầu
    
    Returns:
    --------
    dict: Kết quả backtest
    """
    # Tạo tín hiệu
    signals = strategy.generate_signals(df.copy())
    df['Signal'] = signals
    
    # Tính toán vị thế và lợi nhuận
    capital = initial_capital
    position = 0
    entry_price = 0
    trades = []
    
    for i in range(len(df)):
        price = df['Close'].iloc[i]
        signal = df['Signal'].iloc[i]
        
        if signal == 1 and position == 0:  # Mua
            position = capital / price
            entry_price = price
            trades.append({
                'type': 'buy',
                'date': df.index[i],
                'price': price,
                'capital': capital
            })
        
        elif signal == -1 and position > 0:  # Bán
            capital = position * price
            pnl = ((price - entry_price) / entry_price) * 100
            trades.append({
                'type': 'sell',
                'date': df.index[i],
                'price': price,
                'capital': capital,
                'pnl': pnl
            })
            position = 0
    
    # Tính toán metrics
    if position > 0:  # Đóng vị thế cuối cùng
        final_price = df['Close'].iloc[-1]
        capital = position * final_price
    
    total_return = ((capital - initial_capital) / initial_capital) * 100
    winning_trades = [t for t in trades if t.get('pnl', 0) > 0]
    losing_trades = [t for t in trades if t.get('pnl', 0) < 0]
    
    win_rate = len(winning_trades) / len([t for t in trades if 'pnl' in t]) * 100 if trades else 0
    avg_win = np.mean([t['pnl'] for t in winning_trades]) if winning_trades else 0
    avg_loss = np.mean([t['pnl'] for t in losing_trades]) if losing_trades else 0
    
    # Tính số ngày trong range
    range_days = len(df) / 24  # Giả sử timeframe là 1h
    
    return {
        'initial_capital': initial_capital,
        'final_capital': capital,
        'total_return': total_return,
        'total_trades': len([t for t in trades if 'pnl' in t]),
        'winning_trades': len(winning_trades),
        'losing_trades': len(losing_trades),
        'win_rate': win_rate,
        'avg_win': avg_win,
        'avg_loss': avg_loss,
        'profit_factor': abs(avg_win / avg_loss) if avg_loss != 0 else 0,
        'trades': trades,
        'range_days': range_days
    }

# Ví dụ sử dụng
import yfinance as yf

# Lấy dữ liệu
data = yf.download('EURUSD=X', period='6mo', interval='1h')
df = pd.DataFrame(data)
df.columns = [col.lower() for col in df.columns]

# Chạy backtest
strategy = RSIRangeStrategy(rsi_period=14, range_period=20)
results = backtest_range_strategy(df, strategy, initial_capital=10000)

print(f"Total Return: {results['total_return']:.2f}%")
print(f"Win Rate: {results['win_rate']:.2f}%")
print(f"Total Trades: {results['total_trades']}")
print(f"Profit Factor: {results['profit_factor']:.2f}")

5. Tối ưu hóa tham số Range-Bound Strategy

5.1. Tìm tham số tối ưu

from itertools import product

def optimize_range_parameters(df, strategy_class, param_ranges):
    """
    Tối ưu hóa tham số Range-Bound Strategy
    
    Parameters:
    -----------
    df : pd.DataFrame
        Dữ liệu lịch sử
    strategy_class : class
        Lớp chiến lược
    param_ranges : dict
        Phạm vi tham số cần tối ưu
    
    Returns:
    --------
    dict: Tham số tối ưu và kết quả
    """
    best_params = None
    best_score = -float('inf')
    best_results = None
    
    # Tạo tất cả các tổ hợp tham số
    param_names = list(param_ranges.keys())
    param_values = list(param_ranges.values())
    
    for params in product(*param_values):
        param_dict = dict(zip(param_names, params))
        
        # Tạo chiến lược với tham số mới
        strategy = strategy_class(**param_dict)
        
        # Backtest
        results = backtest_range_strategy(df, strategy)
        
        # Đánh giá (kết hợp return và win rate)
        score = results['total_return'] * (results['win_rate'] / 100)
        
        if score > best_score:
            best_score = score
            best_params = param_dict
            best_results = results
    
    return {
        'best_params': best_params,
        'best_score': best_score,
        'results': best_results
    }

# Ví dụ tối ưu hóa
param_ranges = {
    'rsi_period': [10, 14, 21],
    'range_period': [15, 20, 30],
    'oversold': [25, 30, 35],
    'overbought': [65, 70, 75]
}

optimization_results = optimize_range_parameters(df, RSIRangeStrategy, param_ranges)
print("Best Parameters:", optimization_results['best_params'])
print("Best Score:", optimization_results['best_score'])

6. Quản lý rủi ro với Range-Bound Trading

6.1. Position Sizing động

class RangeRiskManager:
    """Quản lý rủi ro cho chiến lược Range-Bound"""
    
    def __init__(self, max_risk_per_trade=0.02, max_portfolio_risk=0.1):
        self.max_risk_per_trade = max_risk_per_trade
        self.max_portfolio_risk = max_portfolio_risk
    
    def calculate_position_size(self, account_balance, entry_price, 
                                support, resistance):
        """
        Tính toán kích thước vị thế dựa trên range width
        
        Parameters:
        -----------
        account_balance : float
            Số dư tài khoản
        entry_price : float
            Giá vào lệnh
        support : float
            Mức hỗ trợ
        resistance : float
            Mức kháng cự
        
        Returns:
        --------
        float: Kích thước vị thế
        """
        range_width = resistance - support
        stop_loss_distance = range_width * 0.1  # Stop loss = 10% range width
        
        risk_amount = account_balance * self.max_risk_per_trade
        position_size = risk_amount / stop_loss_distance
        
        return position_size
    
    def calculate_stop_loss_take_profit(self, entry_price, support, resistance, 
                                        side='long'):
        """
        Tính stop loss và take profit dựa trên range
        
        Parameters:
        -----------
        entry_price : float
            Giá vào lệnh
        support : float
            Mức hỗ trợ
        resistance : float
            Mức kháng cự
        side : str
            'long' hoặc 'short'
        
        Returns:
        --------
        tuple: (stop_loss, take_profit)
        """
        range_width = resistance - support
        
        if side == 'long':
            # Stop loss dưới support một chút
            stop_loss = support - (range_width * 0.05)
            # Take profit gần resistance
            take_profit = resistance - (range_width * 0.1)
        else:  # short
            # Stop loss trên resistance một chút
            stop_loss = resistance + (range_width * 0.05)
            # Take profit gần support
            take_profit = support + (range_width * 0.1)
        
        return stop_loss, take_profit

6.2. Xác định thị trường Range

def detect_range_market(df, period=50, adx_threshold=25):
    """
    Phát hiện thị trường có phải range không
    
    Parameters:
    -----------
    df : pd.DataFrame
        Dữ liệu OHLCV
    period : int
        Period để tính toán
    adx_threshold : float
        Ngưỡng ADX (ADX < threshold = range market)
    
    Returns:
    --------
    bool: True nếu là range market
    """
    if len(df) < period + 14:
        return False
    
    # Tính ADX
    adx = ta.adx(df['High'], df['Low'], df['Close'], length=14)
    
    if adx is None or len(adx) == 0:
        return False
    
    current_adx = adx.iloc[-1, 0] if isinstance(adx, pd.DataFrame) else adx.iloc[-1]
    
    # Kiểm tra độ biến động giá
    price_range = df['High'].tail(period).max() - df['Low'].tail(period).min()
    price_mean = df['Close'].tail(period).mean()
    volatility = (price_range / price_mean) * 100
    
    # Range market nếu ADX thấp và volatility không quá cao
    return current_adx < adx_threshold and volatility < 5

7. Kết luận: Chiến lược Range-Bound nào hiệu quả nhất?

Đánh giá các chiến lược:

  1. Support/Resistance Cơ bản
    • ✅ Đơn giản, dễ triển khai
    • ❌ Nhiều false signals
    • ⭐ Hiệu quả: 3/5
  2. Bollinger Bands Range
    • ✅ Giảm false signals đáng kể
    • ✅ Phù hợp nhiều thị trường
    • ⭐ Hiệu quả: 4/5
  3. RSI Range
    • ✅ Tín hiệu mạnh, độ chính xác cao
    • ✅ Kết hợp tốt với range trading
    • ⭐ Hiệu quả: 4.5/5
  4. Stochastic Range
    • ✅ Tín hiệu rõ ràng, dễ theo dõi
    • ✅ Phù hợp với range market
    • ⭐ Hiệu quả: 4.5/5

Khuyến nghị:

  • Cho người mới bắt đầu: Bollinger Bands Range Strategy
  • Cho trader có kinh nghiệm: RSI Range hoặc Stochastic Range
  • Cho scalping: RSI Range với khung thời gian ngắn (M15, M30)

Lưu ý quan trọng:

  1. Xác định đúng Range: Chỉ trade khi thị trường thực sự trong range
  2. Quản lý rủi ro: Luôn đặt stop loss và take profit
  3. Tránh trade khi breakout: Khi giá breakout khỏi range, đóng lệnh ngay
  4. Backtest kỹ lưỡng: Kiểm tra chiến lược trên dữ liệu lịch sử
  5. Theo dõi và điều chỉnh: Range có thể thay đổi, cần cập nhật support/resistance
  6. Không trade trong tin tức: Range trading không phù hợp với thời điểm có tin tức lớn

8. Tài liệu tham khảo


Lưu ý: Trading có rủi ro. Hãy luôn backtest kỹ lưỡng và bắt đầu với số vốn nhỏ. Bài viết này chỉ mang tính chất giáo dục, không phải lời khuyên đầu tư.