Торговая стратегия на основе улучшенного индикатора СКО.
Timeframe
5m
Direction
Long Only
Stoploss
-100.0%
Trailing Stop
No
ROI
0m: 10000.0%
Interface Version
3
Startup Candles
100
Indicators
1
freqtrade/freqtrade-strategies
import logging
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import numpy as np
from claude_skd_enhanced import ClaudeSkdIndicator, ClaudeSkdVisualizer, SkdTradeManager, SkdConfig
class ClaudeSkdStrategy(IStrategy):
"""
Торговая стратегия на основе улучшенного индикатора СКО.
Особенности:
- Улучшенные фильтры соотношения объемов (1.2x и 1.3x)
- Торговая логика с соотношением риск/прибыль 1:1
- Визуализация входов, выходов, стопов и тейков
- Поддержка разнонаправленных сделок (long и short одновременно)
- Единая конфигурация без дублирования параметров
"""
INTERFACE_VERSION = 3
logger = logging.getLogger(__name__)
# ========== ОСНОВНЫЕ ПАРАМЕТРЫ СТРАТЕГИИ ==========
timeframe = "5m"
startup_candle_count = 100
# Freqtrade требования
minimal_roi = {"0": 100} # Отключаем ROI, используем свои тейки
stoploss = -1.0 # Отключаем глобальный стоплосс, используем свои стопы
process_only_new_candles = True
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = True
# ========== ПАРАМЕТРЫ СКО (через единую конфигурацию) ==========
# Метод определения СКО
skd_method: str = "basic" # "basic" | "zscore" | "percentile" | "rvol" | "stoch"
skd_vol_min: float | None = None
# НОВЫЕ улучшенные фильтры соотношения объемов
skd_min_vol_ratio_prev: float = 1.2 # volume[t] > volume[t-1] * 1.2
skd_min_vol_ratio_next: float = 1.3 # volume[t] > volume[t+1] * 1.3
# Альтернативные методы
skd_percentile_lookback: int = 20
skd_percentile: float = 70.0
skd_rvol_lookback: int = 20
skd_rvol_threshold: float = 1.5
skd_zscore_lookback: int = 20
skd_zscore_threshold: float = 2.0
skd_stoch_lookback: int = 14
skd_stoch_threshold: float = 80.0
# Сигналы и фильтры
skd_signal_lag_bars: int = 1
skd_min_distance_mode: str = "pct"
skd_min_distance_pct: float = 0.0 # 0 = выключено
skd_atr_period: int = 14
skd_min_distance_atr_mult: float = 0.0 # 0 = выключено
skd_filter_ttl_bars: int = 1
# Визуализация линий
skd_level_ttl: int = 5
skd_max_slots: int = 4
skd_slot_cooldown_bars: int = 1
skd_expire_opposite_on_new: bool = True
skd_same_side_latest_only: bool = True
# Торговые параметры
skd_trade_enabled: bool = True
skd_max_open_trades: int = 2 # Макс. кол-во открытых позиций
skd_trade_expire_bars: int = 20 # Принудительное закрытие через N свечей
# ========== ВИЗУАЛИЗАЦИЯ ==========
# Цвета для графика
_BUY_COLOR = "#2ecc71" # зелёный
_SELL_COLOR = "#e74c3c" # красный
_STOP_COLOR = "#f39c12" # оранжевый
_TAKE_COLOR = "#3498db" # синий
_BUY_LINE = {"mode": "lines", "line": {"color": _BUY_COLOR, "width": 2}}
_SELL_LINE = {"mode": "lines", "line": {"color": _SELL_COLOR, "width": 2}}
_BUY_MARKER = {"mode": "markers", "marker": {"symbol": "circle", "size": 10, "color": _BUY_COLOR}}
_SELL_MARKER = {"mode": "markers", "marker": {"symbol": "circle", "size": 10, "color": _SELL_COLOR}}
# Маркеры входов и выходов
_ENTRY_LONG = {"mode": "markers", "marker": {"symbol": "triangle-up", "size": 12, "color": _BUY_COLOR}}
_ENTRY_SHORT = {"mode": "markers", "marker": {"symbol": "triangle-down", "size": 12, "color": _SELL_COLOR}}
_EXIT_MARKER = {"mode": "markers", "marker": {"symbol": "x", "size": 10, "color": "#95a5a6"}}
# Линии стопов и тейков
_STOP_LINE = {"mode": "lines", "line": {"color": _STOP_COLOR, "dash": "dash", "width": 1}}
_TAKE_LINE = {"mode": "lines", "line": {"color": _TAKE_COLOR, "dash": "dash", "width": 1}}
plot_config = {
"main_plot": {
# Маркеры СКО уровней
"skd_buy_marker": {"type": "scatter", "plotly": _BUY_MARKER},
"skd_sell_marker": {"type": "scatter", "plotly": _SELL_MARKER},
# Горизонтальные линии уровней (4 слота для каждой стороны)
"skd_buy_signal_line_0": {"type": "scatter", "plotly": _BUY_LINE},
"skd_buy_signal_line_1": {"type": "scatter", "plotly": _BUY_LINE},
"skd_buy_signal_line_2": {"type": "scatter", "plotly": _BUY_LINE},
"skd_buy_signal_line_3": {"type": "scatter", "plotly": _BUY_LINE},
"skd_sell_signal_line_0": {"type": "scatter", "plotly": _SELL_LINE},
"skd_sell_signal_line_1": {"type": "scatter", "plotly": _SELL_LINE},
"skd_sell_signal_line_2": {"type": "scatter", "plotly": _SELL_LINE},
"skd_sell_signal_line_3": {"type": "scatter", "plotly": _SELL_LINE},
# Торговые сигналы - входы
"entry_long_signal": {"type": "scatter", "plotly": _ENTRY_LONG},
"entry_short_signal": {"type": "scatter", "plotly": _ENTRY_SHORT},
# Торговые сигналы - выходы
"exit_long_signal": {"type": "scatter", "plotly": _EXIT_MARKER},
"exit_short_signal": {"type": "scatter", "plotly": _EXIT_MARKER},
# Линии стопов и тейков
"stop_loss_long": {"type": "scatter", "plotly": _STOP_LINE},
"stop_loss_short": {"type": "scatter", "plotly": _STOP_LINE},
"take_profit_long": {"type": "scatter", "plotly": _TAKE_LINE},
"take_profit_short": {"type": "scatter", "plotly": _TAKE_LINE},
},
"subplots": {
# Объем для визуального контроля СКО
"Volume": {"type": "bar", "color": "#7f8c8d"}
}
}
def __init__(self, config: dict) -> None:
"""Инициализация стратегии с единой конфигурацией"""
super().__init__(config)
# Создаем единую конфигурацию для всех компонентов
self.skd_config = SkdConfig(
# Метод и фильтры
method=self.skd_method,
vol_min=self.skd_vol_min,
min_vol_ratio_prev=self.skd_min_vol_ratio_prev,
min_vol_ratio_next=self.skd_min_vol_ratio_next,
# Альтернативные методы
percentile_lookback=self.skd_percentile_lookback,
percentile=self.skd_percentile,
rvol_lookback=self.skd_rvol_lookback,
rvol_threshold=self.skd_rvol_threshold,
zscore_lookback=self.skd_zscore_lookback,
zscore_threshold=self.skd_zscore_threshold,
stoch_lookback=self.skd_stoch_lookback,
stoch_threshold=self.skd_stoch_threshold,
# Сигналы
signal_lag_bars=self.skd_signal_lag_bars,
min_distance_mode=self.skd_min_distance_mode,
min_distance_pct=self.skd_min_distance_pct,
atr_period=self.skd_atr_period,
min_distance_atr_mult=self.skd_min_distance_atr_mult,
filter_ttl_bars=self.skd_filter_ttl_bars,
# Визуализация
level_ttl=self.skd_level_ttl,
max_slots=self.skd_max_slots,
slot_cooldown_bars=self.skd_slot_cooldown_bars,
expire_opposite_on_new=self.skd_expire_opposite_on_new,
same_side_latest_only=self.skd_same_side_latest_only,
# Торговля
trade_enabled=self.skd_trade_enabled,
max_open_trades=self.skd_max_open_trades,
trade_expire_bars=self.skd_trade_expire_bars,
)
# Инициализация компонентов с единой конфигурацией
self.indicator = ClaudeSkdIndicator(self.skd_config)
self.visualizer = ClaudeSkdVisualizer(self.skd_config)
self.trade_manager = SkdTradeManager(self.skd_config)
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Вычисление индикаторов и подготовка визуализации.
Единая точка обработки без дублирования.
"""
# 1. Вычисляем СКО индикатор
df = self.indicator.compute(dataframe)
# 2. Обрабатываем торговые сигналы
df = self.trade_manager.process_trades(df)
# 3. Готовим визуализацию линий
df = self.visualizer.render_levels(df)
# 4. Добавляем визуальные маркеры для торговых сигналов
df = self._add_trade_visualization(df)
# Логирование статистики
self.logger.info(
f"[Claude-SKD] Найдено СКО: {self.indicator.stats.get('total_skd_found')} | "
f"Создано уровней: {self.indicator.stats.get('levels_created')} | "
f"Отфильтровано по соотношению: {self.indicator.stats.get('levels_filtered_ratio')} | "
f"Отфильтровано по дистанции: {self.indicator.stats.get('levels_filtered_distance')} | "
f"Сделок: {self.trade_manager.stats.get('total_trades')} | "
f"Win Rate: {self.trade_manager.stats.get('win_rate'):.1f}%"
)
return df
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Определение точек входа на основе торговых сигналов.
Поддержка разнонаправленных сделок.
"""
# Long сигналы
dataframe.loc[
(dataframe["trade_enter_long"] == 1),
"enter_long"
] = 1
# Short сигналы
dataframe.loc[
(dataframe["trade_enter_short"] == 1),
"enter_short"
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Определение точек выхода на основе стопов и тейков.
Управление выходами через trade_manager.
"""
# Long выходы
dataframe.loc[
(dataframe["trade_exit_long"] == 1),
"exit_long"
] = 1
# Short выходы
dataframe.loc[
(dataframe["trade_exit_short"] == 1),
"exit_short"
] = 1
return dataframe
def _add_trade_visualization(self, df: DataFrame) -> DataFrame:
"""
Добавление визуальных маркеров для торговых сигналов.
Линии стопов и тейков для активных сделок.
"""
# Маркеры входов
df["entry_long_signal"] = np.where(
df["trade_enter_long"] == 1,
df["skd_buy_price"],
np.nan
)
df["entry_short_signal"] = np.where(
df["trade_enter_short"] == 1,
df["skd_sell_price"],
np.nan
)
# Маркеры выходов
df["exit_long_signal"] = np.where(
df["trade_exit_long"] == 1,
df["close"],
np.nan
)
df["exit_short_signal"] = np.where(
df["trade_exit_short"] == 1,
df["close"],
np.nan
)
# Линии стопов и тейков (продлеваем их визуально)
df["stop_loss_long"] = self._extend_line(df["trade_stop_long"], periods=5)
df["stop_loss_short"] = self._extend_line(df["trade_stop_short"], periods=5)
df["take_profit_long"] = self._extend_line(df["trade_take_long"], periods=5)
df["take_profit_short"] = self._extend_line(df["trade_take_short"], periods=5)
return df
@staticmethod
def _extend_line(series: DataFrame, periods: int = 5) -> DataFrame:
"""
Продление линии на N периодов вперед для визуализации.
Помогает видеть уровни стопов и тейков на графике.
"""
result = series.copy()
for i in range(len(series)):
if not np.isnan(series.iloc[i]):
# Продлеваем значение на N свечей вперед
end_idx = min(i + periods, len(series))
for j in range(i, end_idx):
if np.isnan(result.iloc[j]):
result.iloc[j] = series.iloc[i]
return result
def custom_exit(self, pair: str, trade, current_time, current_rate,
current_profit, **kwargs) -> str | None:
"""
Кастомная логика выхода для точного контроля стопов и тейков.
"""
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if dataframe.empty:
return None
last_candle = dataframe.iloc[-1]
# Для long позиций
if trade.is_open and trade.is_long:
stop_price = last_candle.get("trade_stop_long")
take_price = last_candle.get("trade_take_long")
if not np.isnan(stop_price) and current_rate <= stop_price:
return "stop_loss_hit"
if not np.isnan(take_price) and current_rate >= take_price:
return "take_profit_hit"
# Для short позиций
elif trade.is_open and trade.is_short:
stop_price = last_candle.get("trade_stop_short")
take_price = last_candle.get("trade_take_short")
if not np.isnan(stop_price) and current_rate >= stop_price:
return "stop_loss_hit"
if not np.isnan(take_price) and current_rate <= take_price:
return "take_profit_hit"
return None