DEV Community

Henry Lin
Henry Lin

Posted on

Lesson 24.2: Freqtrade-Live Trading Stop Loss Operations Detailed Guide

Lesson 24.2: Live Trading Stop Loss Operations Detailed Guide

⏱ Duration: 2 hours
🎯 Learning Objectives: Master various stop loss strategies, learn effective risk control in live trading


Course Overview

Stop loss is the most important risk control tool in live trading. A trading system without a stop loss strategy is like a car without brakes.

Key Focus of This Lesson:

Stop loss is not failure, but protection of capital to keep trading going.

This lesson will cover in detail:

  • Implementation and application of various stop loss types
  • Stop loss configuration in Freqtrade
  • Dynamic and trailing stop losses
  • Stop loss optimization techniques

Part 1: Stop Loss Types Explained

1.1 Fixed Stop Loss

1.1.1 Basic Concept

Fixed stop loss is the simplest stop loss method, setting a fixed percentage based on the entry price.

Calculation Formula:

Stop Loss Price = Entry Price × (1 - Stop Loss Percentage)
Enter fullscreen mode Exit fullscreen mode

Example:

Entry Price: $30,000
Stop Loss: -5%
Stop Loss Price: $30,000 × (1 - 0.05) = $28,500
Enter fullscreen mode Exit fullscreen mode

1.1.2 Freqtrade Configuration

{
  "stoploss": -0.05,
  "stoploss_type": "standard",
  "stoploss_on_exchange": true,
  "stoploss_on_exchange_interval": 60
}
Enter fullscreen mode Exit fullscreen mode

Strategy Example:

class FixedStopLossStrategy(IStrategy):
    # Set 5% fixed stop loss
    stoploss = -0.05

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Buy signal logic
        return dataframe
Enter fullscreen mode Exit fullscreen mode

1.1.3 Pros and Cons Analysis

Advantages:

✅ Simple and intuitive, easy to understand
✅ Simple calculation, low system overhead
✅ Suitable for beginners
✅ Stable performance in normal markets
Enter fullscreen mode Exit fullscreen mode

Disadvantages:

❌ Cannot adapt to market volatility changes
❌ May be triggered by normal fluctuations
❌ May be too aggressive in high-volatility markets
❌ Cannot track profits
Enter fullscreen mode Exit fullscreen mode

1.2 Dynamic Stop Loss

1.2.1 ATR-Based Stop Loss

ATR (Average True Range): Average True Range, measures market volatility.

Calculation Formula:

ATR = Average of true ranges over past N days
Stop Loss Price = Current Price - (ATR × N times multiplier)
Enter fullscreen mode Exit fullscreen mode

Strategy Implementation:

import pandas as pd
import talib.abstract as ta

class ATRStopLossStrategy(IStrategy):
    # Use dynamic stop loss
    use_custom_stoploss = True

    # Minimum stop loss
    stoploss = -0.10

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        # Get historical data
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        # Calculate ATR
        atr = last_candle['atr']

        # Calculate dynamic stop loss (2×ATR)
        atr_multiplier = 2
        stoploss_pct = (atr * atr_multiplier) / current_rate

        # Ensure stop loss does not exceed -15%
        stoploss_pct = min(stoploss_pct, 0.15)

        # Ensure stop loss is not less than -2%
        stoploss_pct = max(stoploss_pct, 0.02)

        return -stoploss_pct

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Calculate ATR indicator
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)

        return dataframe
Enter fullscreen mode Exit fullscreen mode

1.2.2 Volatility-Based Stop Loss

Concept: Adjust stop loss distance based on historical volatility.

import numpy as np

class VolatilityStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.15

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

        # Calculate standard deviation of 20-day returns
        returns = dataframe['close'].pct_change().dropna()
        volatility = returns.tail(20).std() * np.sqrt(252)  # Annualized volatility

        # Adjust stop loss based on volatility
        if volatility > 0.5:  # High volatility
            multiplier = 2.0
        elif volatility > 0.3:  # Medium volatility
            multiplier = 1.5
        else:  # Low volatility
            multiplier = 1.0

        base_stoploss = 0.03  # Base 3% stop loss
        stoploss_pct = base_stoploss * multiplier

        return -min(stoploss_pct, 0.15)  # Maximum -15%
Enter fullscreen mode Exit fullscreen mode

1.3 Trailing Stop Loss

1.3.1 Basic Principle

Trailing stop loss adjusts the stop loss price as price moves favorably, but never adjusts downward.

Example:

Entry Price: $30,000
Setting: 5% trailing stop loss

Price rises to $31,000: Stop loss adjusted to $29,450 ($31,000 × 0.95)
Price rises to $32,000: Stop loss adjusted to $30,400 ($32,000 × 0.95)
Price drops to $31,500: Stop loss remains $30,400
Price drops to $30,400: Stop loss triggered
Enter fullscreen mode Exit fullscreen mode

1.3.2 Freqtrade Implementation

class TrailingStopLossStrategy(IStrategy):
    # Enable trailing stop loss
    trailing_stop = True
    trailing_stop_positive = 0.01  # 1% trailing after profit
    trailing_stop_positive_offset = 0.03  # Start trailing after 3% profit
    trailing_only_offset_is_reached = True  # Only start trailing after offset reached

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Buy signals
        return dataframe
Enter fullscreen mode Exit fullscreen mode

Configuration Explanation:

  • trailing_stop: Enable trailing stop loss
  • trailing_stop_positive: Trailing distance after profit
  • trailing_stop_positive_offset: Profit threshold to start trailing
  • trailing_only_offset_is_reached: Only start trailing after offset reached

1.3.3 Custom Trailing Stop Loss

class CustomTrailingStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.10

    def __init__(self, config: dict) -> None:
        super().__init__(config)
        # Store highest price for each trade
        self.highest_prices = {}

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        # Get trade ID
        trade_id = trade.id

        # Initialize highest price
        if trade_id not in self.highest_prices:
            self.highest_prices[trade_id] = trade.open_rate

        # Update highest price
        if current_rate > self.highest_prices[trade_id]:
            self.highest_prices[trade_id] = current_rate

        highest_price = self.highest_prices[trade_id]

        # If current profit exceeds 3%, use trailing stop loss
        if current_profit > 0.03:
            # 2% trailing stop loss
            trailing_distance = 0.02
            stoploss_price = highest_price * (1 - trailing_distance)
            stoploss_pct = (current_rate - stoploss_price) / current_rate

            return -stoploss_pct
        else:
            # Otherwise use 5% fixed stop loss
            return -0.05
Enter fullscreen mode Exit fullscreen mode

Part 2: Advanced Stop Loss Strategies

2.1 Technical Indicator Stop Loss

2.1.1 Moving Average Stop Loss

class MAStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.15

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        # Use 20-day moving average as stop loss
        ma20 = last_candle['ma20']

        if current_rate > ma20:
            # Current price above MA, use MA stop loss
            stoploss_pct = (current_rate - ma20) / current_rate
            return -min(stoploss_pct, 0.15)
        else:
            # Price below MA, use fixed stop loss
            return -0.08

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['ma20'] = ta.SMA(dataframe, timeperiod=20)
        dataframe['ma50'] = ta.SMA(dataframe, timeperiod=50)
        return dataframe
Enter fullscreen mode Exit fullscreen mode

2.1.2 Bollinger Bands Stop Loss

class BollingerStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.15

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        # Use Bollinger Band lower band as stop loss
        lower_band = last_candle['bb_lowerband']

        # Ensure stop loss price not lower than BB lower band
        if current_rate > lower_band:
            stoploss_pct = (current_rate - lower_band) / current_rate
            return -min(stoploss_pct, 0.20)
        else:
            return -0.10

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']
        return dataframe
Enter fullscreen mode Exit fullscreen mode

2.1.3 Support Level Stop Loss

class SupportStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.15

    def find_support_levels(self, dataframe: DataFrame):
        """Find support levels"""
        # Simple support level identification: local lows
        lows = dataframe['low']

        # Find support levels (simplified version)
        support_levels = []
        for i in range(2, len(lows) - 2):
            current_low = lows.iloc[i]
            if (current_low < lows.iloc[i-1] and
                current_low < lows.iloc[i-2] and
                current_low < lows.iloc[i+1] and
                current_low < lows.iloc[i+2]):
                support_levels.append(current_low)

        return sorted(support_levels, reverse=True)[:5]  # Return top 5 support levels

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        support_levels = self.find_support_levels(dataframe)

        if support_levels:
            # Use nearest support level as stop loss
            nearest_support = support_levels[0]

            if current_rate > nearest_support:
                stoploss_pct = (current_rate - nearest_support) / current_rate
                # At least 2% stop loss, maximum 15%
                stoploss_pct = max(stoploss_pct, 0.02)
                stoploss_pct = min(stoploss_pct, 0.15)
                return -stoploss_pct

        return -0.08  # Default stop loss
Enter fullscreen mode Exit fullscreen mode

2.2 Time Stop Loss

2.2.1 Holding Time Stop Loss

import datetime

class TimeStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.15

    # Maximum holding time (hours)
    max_hold_time = 24  # 24 hours

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        # Calculate holding time
        hold_time = current_time - trade.open_date

        # If holding time exceeds limit, reduce stop loss
        if hold_time > datetime.timedelta(hours=self.max_hold_time):
            if current_profit > 0:
                # Holding too long with profit, use small stop loss for quick exit
                return -0.01
            else:
                # Holding too long with loss, stop loss exit
                return -0.001

        # Normal stop loss logic
        if current_profit > 0.05:
            return -0.03  # Profit over 5%, 3% stop loss
        else:
            return -0.08  # Default stop loss
Enter fullscreen mode Exit fullscreen mode

2.2.2 Hourly Stop Loss

class HourlyStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.15

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        hour = current_time.hour

        # Adjust stop loss based on time period
        if 0 <= hour < 6:  # Late night, lower volatility
            if current_profit > 0:
                return -0.02  # Tighter stop loss
            else:
                return -0.05

        elif 6 <= hour < 12:  # Morning session
            return -0.08

        elif 12 <= hour < 18:  # Afternoon session, higher volatility
            return -0.10

        else:  # Evening session
            return -0.06
Enter fullscreen mode Exit fullscreen mode

2.3 Combined Stop Loss Strategies

2.3.1 Multi-Condition Stop Loss

class CombinedStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.20

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        # 1. ATR stop loss
        atr = last_candle['atr']
        atr_stoploss = (atr * 2) / current_rate

        # 2. Moving average stop loss
        ma20 = last_candle['ma20']
        if current_rate > ma20:
            ma_stoploss = (current_rate - ma20) / current_rate
        else:
            ma_stoploss = 0.15

        # 3. Time stop loss
        hold_time = current_time - trade.open_date
        if hold_time > datetime.timedelta(hours=12):
            time_stoploss = 0.03 if current_profit > 0 else 0.08
        else:
            time_stoploss = 0.15

        # 4. Profit trailing stop loss
        if current_profit > 0.05:
            trailing_stoploss = 0.02
        else:
            trailing_stoploss = 0.15

        # Take the strictest stop loss (minimum value)
        final_stoploss = min(atr_stoploss, ma_stoploss, time_stoploss, trailing_stoploss)
        final_stoploss = max(final_stoploss, 0.02)  # At least 2%
        final_stoploss = min(final_stoploss, 0.20)  # At most 20%

        return -final_stoploss

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['ma20'] = ta.SMA(dataframe, timeperiod=20)
        return dataframe
Enter fullscreen mode Exit fullscreen mode

Part 3: Stop Loss Execution Mechanisms

3.1 Local Stop Loss vs Exchange Stop Loss

3.1.1 Local Stop Loss

Features:

✅ High flexibility, can implement complex logic
✅ Does not consume exchange API limits
✅ Can monitor and adjust in real-time
❌ Depends on system stability
❌ Network issues may cause delays
❌ Requires system to run continuously
Enter fullscreen mode Exit fullscreen mode

Configuration:

{
  "stoploss_on_exchange": false,
  "stoploss": -0.05
}
Enter fullscreen mode Exit fullscreen mode

3.1.2 Exchange Stop Loss

Features:

✅ Reliable execution, not affected by local system
✅ Fast response speed
✅ Does not consume local resources
❌ Limited functionality, only supports basic stop loss
❌ Consumes API limits
❌ Less flexible adjustment
Enter fullscreen mode Exit fullscreen mode

Configuration:

{
  "stoploss_on_exchange": true,
  "stoploss_on_exchange_interval": 60,
  "stoploss": -0.05
}
Enter fullscreen mode Exit fullscreen mode

3.2 Stop Loss Order Types

3.2.1 Stop Loss Market Order

# Set stop loss market order on exchange
def create_stop_market_order(exchange, symbol, amount, stop_price):
    """
    Create stop loss market order
    """
    try:
        order = exchange.create_order(
            symbol=symbol,
            type='stop_market',
            side='sell',
            amount=amount,
            params={'stopPrice': stop_price}
        )

        print(f"Stop loss market order created: {order['id']}")
        print(f"Trigger price: ${stop_price}")

        return order

    except Exception as e:
        print(f"Failed to create stop loss order: {e}")
        return None
Enter fullscreen mode Exit fullscreen mode

3.2.2 Stop Loss Limit Order

def create_stop_limit_order(exchange, symbol, amount, stop_price, limit_price):
    """
    Create stop loss limit order
    """
    try:
        order = exchange.create_order(
            symbol=symbol,
            type='stop_limit',
            side='sell',
            amount=amount,
            price=limit_price,
            params={'stopPrice': stop_price}
        )

        print(f"Stop loss limit order created: {order['id']}")
        print(f"Trigger price: ${stop_price}")
        print(f"Limit price: ${limit_price}")

        return order

    except Exception as e:
        print(f"Failed to create stop loss order: {e}")
        return None
Enter fullscreen mode Exit fullscreen mode

3.3 Stop Loss Monitoring and Adjustment

3.3.1 Stop Loss Status Monitoring

class StopLossMonitor:
    def __init__(self):
        self.active_stop_orders = {}
        self.stop_history = []

    def add_stop_order(self, trade_id, stop_price, order_type='market'):
        """Add stop loss order"""
        self.active_stop_orders[trade_id] = {
            'stop_price': stop_price,
            'order_type': order_type,
            'created_time': datetime.now(),
            'adjusted_times': 0
        }

    def update_stop_price(self, trade_id, new_stop_price):
        """Update stop loss price"""
        if trade_id in self.active_stop_orders:
            old_price = self.active_stop_orders[trade_id]['stop_price']
            self.active_stop_orders[trade_id]['stop_price'] = new_stop_price
            self.active_stop_orders[trade_id]['adjusted_times'] += 1

            print(f"Stop loss price updated: {old_price}{new_stop_price}")

    def check_stop_trigger(self, current_price, trade_id):
        """Check if stop loss is triggered"""
        if trade_id not in self.active_stop_orders:
            return False

        stop_price = self.active_stop_orders[trade_id]['stop_price']

        if current_price <= stop_price:
            # Record stop loss history
            self.stop_history.append({
                'trade_id': trade_id,
                'stop_price': stop_price,
                'trigger_price': current_price,
                'trigger_time': datetime.now(),
                'order_type': self.active_stop_orders[trade_id]['order_type']
            })

            # Remove active stop loss
            del self.active_stop_orders[trade_id]
            return True

        return False

    def get_stop_statistics(self):
        """Get stop loss statistics"""
        if not self.stop_history:
            return None

        total_stops = len(self.stop_history)
        market_stops = len([s for s in self.stop_history if s['order_type'] == 'market'])

        return {
            'total_stops': total_stops,
            'market_stops': market_stops,
            'limit_stops': total_stops - market_stops,
            'stop_efficiency': self.calculate_stop_efficiency()
        }

    def calculate_stop_efficiency(self):
        """Calculate stop loss efficiency"""
        # Simplified calculation: difference between actual and set stop loss prices
        if not self.stop_history:
            return 0

        total_slippage = 0
        for stop in self.stop_history:
            slippage = abs(stop['trigger_price'] - stop['stop_price']) / stop['stop_price']
            total_slippage += slippage

        avg_slippage = total_slippage / len(self.stop_history)
        efficiency = max(0, 1 - avg_slippage)  # Efficiency = 1 - average slippage

        return efficiency
Enter fullscreen mode Exit fullscreen mode

3.3.2 Dynamic Stop Loss Adjustment

def dynamic_stop_adjustment(trade, current_price, current_profit, indicators):
    """
    Dynamically adjust stop loss based on market conditions
    """
    original_stop = trade.stop_loss

    # Adjust based on profit
    if current_profit > 0.10:
        # Profit over 10%, tighten stop loss
        new_stop = current_price * 0.97  # 3% stop loss
    elif current_profit > 0.05:
        # Profit 5-10%, moderately tighten
        new_stop = current_price * 0.95  # 5% stop loss
    else:
        # Small profit, keep original stop loss
        new_stop = original_stop

    # Adjust based on volatility
    volatility = indicators.get('volatility', 0.02)
    if volatility > 0.05:  # High volatility
        new_stop = min(new_stop, original_stop * 1.2)  # Widen stop loss
    elif volatility < 0.01:  # Low volatility
        new_stop = max(new_stop, original_stop * 0.8)  # Tighten stop loss

    # Adjust based on technical indicators
    if indicators.get('trend') == 'strong_bullish':
        new_stop = max(new_stop, current_price * 0.95)  # Uptrend, widen stop loss

    return new_stop
Enter fullscreen mode Exit fullscreen mode

Part 4: Stop Loss Optimization Strategies

4.1 Stop Loss Backtest Analysis

4.1.1 Stop Loss Effectiveness Analysis

def analyze_stoploss_effectiveness(trades_dataframe):
    """
    Analyze the effectiveness of stop loss strategies
    """
    total_trades = len(trades_dataframe)
    stopped_trades = trades_dataframe[trades_dataframe['stoploss_reason'].notna()]

    # Stop loss trigger statistics
    stop_count = len(stopped_trades)
    stop_rate = stop_count / total_trades if total_trades > 0 else 0

    # Loss avoided by stop loss
    avg_stop_loss = stopped_trades['profit_ratio'].mean()
    max_potential_loss = stopped_trades['max_drawdown'].mean()
    loss_saved = max_potential_loss - avg_stop_loss

    # Stop loss type distribution
    stop_types = stopped_trades['stoploss_reason'].value_counts()

    analysis = {
        'total_trades': total_trades,
        'stop_count': stop_count,
        'stop_rate': stop_rate,
        'avg_stop_loss': avg_stop_loss,
        'loss_saved_per_trade': loss_saved,
        'total_loss_saved': loss_saved * stop_count,
        'stop_type_distribution': stop_types.to_dict()
    }

    return analysis

# Usage example
import pandas as pd
# Assume we have a trade record DataFrame
trades_df = pd.read_csv('backtest_results.csv')
analysis = analyze_stoploss_effectiveness(trades_df)

print(f"Total trades: {analysis['total_trades']}")
print(f"Stop loss triggers: {analysis['stop_count']}")
print(f"Stop loss trigger rate: {analysis['stop_rate']:.2%}")
print(f"Average loss avoided by stop loss: {analysis['loss_saved_per_trade']:.2%}")
print(f"Total loss avoided by stop loss: {analysis['total_loss_saved']:.2%}")
Enter fullscreen mode Exit fullscreen mode

4.1.2 Different Stop Loss Strategy Comparison

def compare_stoploss_strategies(backtest_results):
    """
    Compare performance of different stop loss strategies
    """
    comparison = {}

    for strategy_name, results in backtest_results.items():
        trades = results['trades']

        # Calculate key metrics
        total_profit = trades['profit_abs'].sum()
        win_rate = len(trades[trades['profit_abs'] > 0]) / len(trades)
        max_drawdown = results['max_drawdown']
        sharpe_ratio = results['sharpe']

        # Stop loss related metrics
        stopped_trades = trades[trades['stoploss_reason'].notna()]
        stop_rate = len(stopped_trades) / len(trades)
        avg_stop_loss = stopped_trades['profit_ratio'].mean() if len(stopped_trades) > 0 else 0

        comparison[strategy_name] = {
            'total_profit': total_profit,
            'win_rate': win_rate,
            'max_drawdown': max_drawdown,
            'sharpe_ratio': sharpe_ratio,
            'stop_rate': stop_rate,
            'avg_stop_loss': avg_stop_loss
        }

    return comparison

# Visualize comparison
def plot_stoploss_comparison(comparison):
    """
    Plot stop loss strategy comparison chart
    """
    import matplotlib.pyplot as plt

    strategies = list(comparison.keys())
    metrics = ['total_profit', 'win_rate', 'max_drawdown', 'sharpe_ratio']

    fig, axes = plt.subplots(2, 2, figsize=(15, 10))
    axes = axes.ravel()

    for i, metric in enumerate(metrics):
        values = [comparison[s][metric] for s in strategies]
        axes[i].bar(strategies, values)
        axes[i].set_title(f'{metric.replace("_", " ").title()}')
        axes[i].tick_params(axis='x', rotation=45)

    plt.tight_layout()
    plt.show()
Enter fullscreen mode Exit fullscreen mode

4.2 Stop Loss Parameter Optimization

4.2.1 Stop Loss Percentage Optimization

def optimize_stoploss_percentage(strategy_class, timeframe, pair_list,
                               stoploss_range=[-0.02, -0.03, -0.04, -0.05, -0.06, -0.08, -0.10]):
    """
    Optimize stop loss percentage
    """
    results = {}

    for stoploss in stoploss_range:
        print(f"Testing stop loss: {stoploss:.1%}")

        # Create strategy instance
        strategy = strategy_class({
            'strategy': strategy_class.__name__,
            'stoploss': stoploss
        })

        # Run backtest
        backtest_result = run_backtest(strategy, timeframe, pair_list)

        results[str(stoploss)] = {
            'total_profit': backtest_result['total_profit'],
            'profit_mean': backtest_result['profit_mean'],
            'win_rate': backtest_result['wins'] / backtest_result['total_trades'],
            'max_drawdown': backtest_result['max_drawdown_abs'],
            'sharpe': backtest_result['sharpe']
        }

    # Find optimal stop loss
    best_stoploss = max(results.keys(),
                       key=lambda x: results[x]['sharpe'])

    return {
        'results': results,
        'best_stoploss': best_stoploss,
        'best_performance': results[best_stoploss]
    }
Enter fullscreen mode Exit fullscreen mode

4.2.2 Trailing Stop Loss Parameter Optimization

def optimize_trailing_stop(strategy_class, timeframe, pair_list,
                          trailing_offsets=[0.01, 0.02, 0.03, 0.04, 0.05],
                          trailing_stops=[0.005, 0.01, 0.015, 0.02]):
    """
    Optimize trailing stop loss parameters
    """
    results = {}

    for offset in trailing_offsets:
        for stop in trailing_stops:
            print(f"Testing trailing parameters: offset={offset:.1%}, stop={stop:.1%}")

            # Create strategy instance
            strategy = strategy_class({
                'strategy': strategy_class.__name__,
                'trailing_stop': True,
                'trailing_stop_positive_offset': offset,
                'trailing_stop_positive': stop
            })

            # Run backtest
            backtest_result = run_backtest(strategy, timeframe, pair_list)

            key = f"offset_{offset:.1%}_stop_{stop:.1%}"
            results[key] = {
                'offset': offset,
                'stop': stop,
                'total_profit': backtest_result['total_profit'],
                'win_rate': backtest_result['wins'] / backtest_result['total_trades'],
                'max_drawdown': backtest_result['max_drawdown_abs'],
                'sharpe': backtest_result['sharpe']
            }

    # Find optimal parameters
    best_params = max(results.keys(),
                     key=lambda x: results[x]['sharpe'])

    return {
        'results': results,
        'best_parameters': best_params,
        'best_performance': results[best_params]
    }
Enter fullscreen mode Exit fullscreen mode

Part 5: Practical Configuration Examples

5.1 Conservative Stop Loss Configuration

Suitable for: Beginners, low-risk preference

{
  "stoploss": -0.06,
  "trailing_stop": false,
  "stoploss_on_exchange": true,
  "stoploss_on_exchange_interval": 60,
  "order_types": {
    "stoploss": "market",
    "stoploss_on_exchange": true,
    "stoploss_on_exchange_interval": 60
  }
}
Enter fullscreen mode Exit fullscreen mode

Features:

  • 6% fixed stop loss, relatively conservative
  • Use exchange stop loss for high reliability
  • No trailing stop loss, simple and direct

5.2 Aggressive Stop Loss Configuration

Suitable for: High-frequency trading, high-risk preference

{
  "stoploss": -0.08,
  "trailing_stop": true,
  "trailing_stop_positive": 0.01,
  "trailing_stop_positive_offset": 0.02,
  "trailing_only_offset_is_reached": true,
  "stoploss_on_exchange": false,
  "order_types": {
    "stoploss": "limit",
    "stoploss_on_exchange": false
  }
}
Enter fullscreen mode Exit fullscreen mode

Features:

  • Start 1% trailing stop loss after 2% profit
  • Use local stop loss for complex logic implementation
  • Stop loss limit order to control execution price

5.3 Intelligent Stop Loss Configuration

Suitable for: Experienced traders

class IntelligentStopLossStrategy(IStrategy):
    use_custom_stoploss = True
    stoploss = -0.15

    # Configuration parameters
    atr_multiplier = 2.0
    trailing_start_profit = 0.03
    trailing_distance = 0.02
    max_stoploss = -0.20
    min_stoploss = -0.02

    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime,
                       current_rate: float, current_profit: float, **kwargs) -> float:

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        # 1. ATR dynamic stop loss
        atr = last_candle.get('atr', current_rate * 0.02)  # Default 2%
        atr_stoploss = (atr * self.atr_multiplier) / current_rate

        # 2. Trailing stop loss
        if current_profit > self.trailing_start_profit:
            trailing_stoploss = self.trailing_distance
        else:
            trailing_stoploss = self.stoploss * -1

        # 3. Time stop loss
        hold_time = current_time - trade.open_date
        if hold_time > datetime.timedelta(hours=48):
            time_stoploss = 0.05 if current_profit > 0 else 0.15
        else:
            time_stoploss = self.stoploss * -1

        # Choose optimal stop loss
        final_stoploss = min(atr_stoploss, trailing_stoploss, time_stoploss)

        # Limit stop loss range
        final_stoploss = max(final_stoploss, self.min_stoploss)
        final_stoploss = min(final_stoploss, abs(self.max_stoploss))

        return -final_stoploss
Enter fullscreen mode Exit fullscreen mode

📝 Practical Tasks

Task 1: Stop Loss Strategy Testing

  1. Test different stop loss percentages (2%, 3%, 5%, 8%, 10%) in backtest
  2. Record win rate, return rate, maximum drawdown for each stop loss
  3. Choose the stop loss setting most suitable for your strategy

Task 2: Trailing Stop Loss Optimization

  1. Test different trailing parameter combinations
  2. Analyze the impact of trailing stop loss on return rate
  3. Find optimal trailing distance and trigger conditions

Task 3: Live Stop Loss Verification

  1. Test stop loss configuration with small capital in live trading
  2. Observe timeliness and accuracy of stop loss triggers
  3. Record actual slippage situations
  4. Adjust parameters based on live performance

📌 Key Points

Stop Loss Setting Principles

1. Protect Capital First
   ✅ Stop loss is a tool to protect capital
   ✅ Any stop loss is better than no stop loss
   ✅ Don't set stop loss too large due to fear of stops

2. Adapt to Strategy Characteristics
   ✅ Trend strategies need looser stop losses
   ✅ Range-bound strategies need tighter stop losses
   ✅ High-frequency strategies need fast stops

3. Consider Market Environment
   ✅ Loosen stops in high-volatility markets
   ✅ Tighten stops in low-volatility markets
   ✅ Adjust stops before major events
Enter fullscreen mode Exit fullscreen mode

Common Stop Loss Mistakes

Mistake Consequence Correct Approach
Stop loss set too large Excessive losses Set reasonably based on strategy
Stop loss set too small Frequent stops Consider normal volatility range
Moving stops after profit May exit too early Use trailing stop loss
Ignoring stop loss execution Actual losses exceed expectations Ensure reliable stop loss execution

Stop Loss Optimization Checklist

□ Verify stop loss effectiveness through backtest
□ Test performance under different market conditions
□ Optimize stop loss parameters
□ Ensure reliable stop loss execution mechanism
□ Verify stop loss performance in live trading
□ Regularly review and adjust stop loss strategies
Enter fullscreen mode Exit fullscreen mode

🎯 Next Lesson Preview

Lesson 24.3: Futures Trading Operations Detailed Guide

In the next lesson, we will learn:

  • Binance futures trading API usage
  • Leverage and margin management
  • Futures order types and operations
  • Capital management and risk control

After mastering spot trading stop loss techniques, we will enter the higher-risk futures trading domain.

Top comments (0)