레슨 29: 머신러닝 및 전략 최적화

발행: (2025년 12월 3일 오후 12:03 GMT+9)
4 min read
원문: Dev.to

Source: Dev.to

⏱ Duration: 2.5 시간

🎯 학습 목표

  • 머신러닝을 활용하여 전략 개발 및 파라미터 최적화를 지원하는 방법을 학습합니다

강좌 개요

머신러닝(ML)은 다음과 같이 도와줄 수 있습니다:

  • 🔍 데이터에서 숨겨진 패턴 발견
  • 🎯 전략 파라미터 최적화
  • 📊 가격 추세 예측
  • 🤖 적응형 전략 구축

Important Reminders

  • ⚠️ 머신러닝은 ‘만능 해결책’이 아니며 수익을 보장하지 못합니다.
  • ⚠️ 대량의 데이터와 계산 자원이 필요합니다.
  • ⚠️ 과적합이 발생하기 쉬우며, 신중히 검증해야 합니다.
  • ⚠️ 이 강의는 이론적 깊이보다는 실용적인 방법에 초점을 맞춥니다.

Part 1: Freqtrade의 Hyperopt

1.1 Hyperopt 소개

Hyperopt는 머신러닝 알고리즘을 사용해 최적 파라미터를 자동으로 찾는 Freqtrade의 내장 파라미터 최적화 도구입니다.

기본 개념

What is Hyperopt?
- Automated parameter search
- Uses Bayesian optimization algorithms
- Searches for optimal parameters within specified ranges
- Scores based on backtest results

What can be optimized?
- Buy condition parameters (RSI thresholds, EMA periods)
- Sell condition parameters
- ROI configuration
- Stop loss configuration
- Trailing stop loss configuration

최적화 공간

Freqtrade supports 5 optimization spaces:

1. buy      – Buy condition parameters
2. sell     – Sell condition parameters
3. roi      – ROI configuration
4. stoploss – Stop loss configuration
5. trailing – Trailing stop loss configuration

Can optimize individually or in combination.

1.2 Hyperopt 호환 전략 준비

전략 파일을 생성합니다. 예: user_data/strategies/HyperoptableStrategy.py:

from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, CategoricalParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from functools import reduce

class HyperoptableStrategy(IStrategy):
    """
    Hyperopt‑compatible strategy
    Define optimizable parameter ranges
    """
    INTERFACE_VERSION = 3

    # ===== Optimizable Parameters =====
    # Buy parameters
    buy_rsi_threshold = IntParameter(20, 40, default=30, space='buy')
    buy_rsi_enabled   = CategoricalParameter([True, False], default=True, space='buy')
    buy_ema_short     = IntParameter(5, 20, default=9, space='buy')
    buy_ema_long      = IntParameter(15, 50, default=21, space='buy')

    # Sell parameters
    sell_rsi_threshold = IntParameter(60, 80, default=70, space='sell')
    sell_rsi_enabled   = CategoricalParameter([True, False], default=True, space='sell')

    # ROI parameters (static example)
    minimal_roi = {
        "0": 0.10,
        "30": 0.05,
        "60": 0.03,
        "120": 0.01
    }

    # Stop loss parameter
    stoploss = -0.10

    # Trailing stop loss parameters
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.02
    trailing_only_offset_is_reached = True

    timeframe = '5m'
    startup_candle_count: int = 50

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # EMA (using optimizable periods)
        for val in self.buy_ema_short.range:
            dataframe[f'ema_short_{val}'] = ta.EMA(dataframe, timeperiod=val)
        for val in self.buy_ema_long.range:
            dataframe[f'ema_long_{val}'] = ta.EMA(dataframe, timeperiod=val)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)

        # Volume mean
        dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean()
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []

        # EMA golden cross
        conditions.append(
            qtpylib.crossed_above(
                dataframe[f'ema_short_{self.buy_ema_short.value}'],
                dataframe[f'ema_long_{self.buy_ema_long.value}']
            )
        )

        # RSI (if enabled)
        if self.buy_rsi_enabled.value:
            conditions.append(dataframe['rsi'] > self.buy_rsi_threshold.value)
            conditions.append(dataframe['rsi']  dataframe['volume_mean'])
        conditions.append(dataframe['volume'] > 0)

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []

        # EMA death cross
        conditions.append(
            qtpylib.crossed_below(
                dataframe[f'ema_short_{self.buy_ema_short.value}'],
                dataframe[f'ema_long_{self.buy_ema_long.value}']
            )
        )

        # RSI (if enabled)
        if self.sell_rsi_enabled.value:
            conditions.append(dataframe['rsi'] > self.sell_rsi_threshold.value)

        # Volume filter
        conditions.append(dataframe['volume'] > 0)

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1
        return dataframe

핵심 포인트

# Integer parameter
buy_rsi_threshold = IntParameter(20, 40, default=30, space='buy')
# Search within 20‑40 range, default 30

# Decimal parameter
stoploss = DecimalParameter(-0.15, -0.05, default=-0.10, space='stoploss')
# Search within -0.15 to -0.05 range

# Categorical parameter (True/False)
buy_rsi_enabled = CategoricalParameter([True, False], default=True, space='buy')

1.3 Hyperopt 실행

기본 명령

# Optimize buy parameters only
freqtrade hyperopt \
    -c config.json \
    --strategy HyperoptableStrategy \
    --hyperopt-loss SharpeHyperOptLoss \
    --spaces buy \
    --epochs 100
  • --hyperopt-loss – 최적화 목표
Back to Blog

관련 글

더 보기 »

계정 전환

@blink_c5eb0afe3975https://dev.to/blink_c5eb0afe3975 여러분도 알다시피 저는 다시 제 진행 상황을 기록하기 시작했으니, 이것을 다른…