Files
Chan/strategies/ChanSameLevelStrategy.py
T
2026-03-06 22:08:24 +08:00

665 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
缠论同级别分解策略 (Chan Same-Level Decomposition Strategy)
核心思想:按同级别分解操作,实现a+A结构的机械化操作
以5分钟级别为例:
1. a+A结构:a是5分钟走势类型(定义为A0),A分解为m段5分钟走势类型:A=A1+A2+...+Am
2. 如果a+A向上,则Ai当i为奇数时向下,i为偶数时向上
3. 中枢形成:
- A1不能跌破a的低点
- 如果A2升破a的高点而A3不跌回a的高点,可以把a+A1+A2+A3当成一个新的a'(还是5分钟级别)
- 如果A3跌破a的高点,则A1、A2、A3必然构成30分钟中枢
操作程式(机械化操作):
1. 盘整背驰情况:
- Ai与Ai+2之间比较力度(盘整背驰)
- i+2为偶数时卖出
- i+2为奇数时买入
2. 非背驰情况:
- 当i为偶数,若Ai+3不跌破Ai高点,则继续持有到Ai+k+3跌破Ai+k高点后在不创新高或盘整顶背驰的Ai+k+4卖出,其中k为偶数
- 当i为奇数,若Ai+3不升破Ai低点,则继续保持不回补直到Ai+k+3升破Ai+k低点后在不创新低或盘整底背驰的Ai+k+4回补
使用命令:
freqtrade backtesting -c ./user_data/Chan/config/Local_Test.json \
--strategy ChanSameLevelStrategy --strategy-path ./user_data/Chan/strategies \
--timerange=20250301-
"""
import logging
from datetime import datetime
from typing import Optional
import numpy as np
import pandas as pd
import talib.abstract as ta
from pandas import DataFrame
from technical.util import resample_to_interval, resampled_merge
from freqtrade.strategy import IStrategy
logger = logging.getLogger(__name__)
class ChanSameLevelStrategy(IStrategy):
INTERFACE_VERSION: int = 3
# === 基础配置 ===
# 底层使用 1m K线,resample 到 30m 进行同级别分解
can_short = True
startup_candle_count: int = 2000 # 需要足够的数据来识别走势段
# 止损和止盈(优化:改善风险回报比)
stoploss = -0.015 # 1.5% 硬止损(更紧,减少单笔亏损)
use_custom_stoploss = False
# Trailing stop(优化:更激进的保护利润)
trailing_stop = True
trailing_stop_positive = 0.006 # 回撤 0.6% 触发退出(更紧)
trailing_stop_positive_offset = 0.012 # 盈利 1.2% 后才开始追踪(降低门槛)
trailing_only_offset_is_reached = True
# ROI(优化:更合理的止盈目标,改善风险回报比)
minimal_roi = {
"0": 0.03, # 3% 立即止盈(降低目标,提高胜率)
"60": 0.02, # 60分钟后 2%
"120": 0.015, # 120分钟后 1.5%
"240": 0.01, # 240分钟后 1%
"480": 0.005, # 480分钟后 0.5%
"720": 0, # 720分钟后不设止盈
}
order_types = {
"entry": "market",
"exit": "market",
"stoploss": "market",
"stoploss_on_exchange": False,
}
# 同级别分解的级别(5分钟)
same_level_timeframe = 5
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""计算指标并识别同级别走势段"""
ticker = self.get_ticker_indicator()
# Resample 到 30m 进行同级别分解
dataframe_30m = resample_to_interval(dataframe, ticker * self.same_level_timeframe)
# 在 30m 上计算指标
dataframe_30m = self.add_indicators_30m(dataframe_30m)
# 识别同级别走势段和背驰
dataframe_30m = self.identify_same_level_segments(dataframe_30m)
# 合并回 1m dataframe
dataframe = resampled_merge(dataframe, dataframe_30m)
# 在 1m 上也计算基础指标
dataframe = self.add_indicators_1m(dataframe)
return dataframe
def add_indicators_30m(self, dataframe: DataFrame) -> DataFrame:
"""在30m级别计算指标"""
# MACD 用于识别背驰
macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
# EMA 用于识别趋势(增加更多EMA用于趋势确认)
dataframe['ema12'] = ta.EMA(dataframe, timeperiod=12)
dataframe['ema26'] = ta.EMA(dataframe, timeperiod=26)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['ema200'] = ta.EMA(dataframe, timeperiod=200) # 长期趋势
# EMA趋势方向
dataframe['ema_trend_up'] = (dataframe['ema12'] > dataframe['ema26']) & (dataframe['ema26'] > dataframe['ema50'])
dataframe['ema_trend_dn'] = (dataframe['ema12'] < dataframe['ema26']) & (dataframe['ema26'] < dataframe['ema50'])
# 市场整体趋势(基于价格和EMA200)
dataframe['price_above_ema200'] = dataframe['close'] > dataframe['ema200']
dataframe['price_below_ema200'] = dataframe['close'] < dataframe['ema200']
# 趋势强度(EMA斜率)
dataframe['ema12_slope'] = dataframe['ema12'].diff(5) / dataframe['ema12'].shift(5)
dataframe['ema26_slope'] = dataframe['ema26'].diff(5) / dataframe['ema26'].shift(5)
dataframe['strong_uptrend'] = (dataframe['ema12_slope'] > 0) & (dataframe['ema26_slope'] > 0) & (dataframe['price_above_ema200'])
dataframe['strong_downtrend'] = (dataframe['ema12_slope'] < 0) & (dataframe['ema26_slope'] < 0) & (dataframe['price_below_ema200'])
# RSI 用于确认
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
# ATR 用于波动率过滤
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
dataframe['atr_mean'] = dataframe['atr'].rolling(window=20).mean()
# 波动率过滤:只在波动率足够时交易
dataframe['volatility_ok'] = dataframe['atr'] > dataframe['atr_mean'] * 0.8
return dataframe
def add_indicators_1m(self, dataframe: DataFrame) -> DataFrame:
"""在1m级别计算基础指标"""
dataframe['rsi_1m'] = ta.RSI(dataframe, timeperiod=14)
dataframe['volume_mean'] = dataframe['volume'].rolling(window=20).mean()
# MACD 用于1m级别确认
macd_1m = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
dataframe['macd_1m'] = macd_1m['macd']
dataframe['macdsignal_1m'] = macd_1m['macdsignal']
dataframe['macdhist_1m'] = macd_1m['macdhist']
# MACD交叉确认
dataframe['macd_cross_up_1m'] = (
(dataframe['macd_1m'] > dataframe['macdsignal_1m']) &
(dataframe['macd_1m'].shift(1) <= dataframe['macdsignal_1m'].shift(1))
)
dataframe['macd_cross_dn_1m'] = (
(dataframe['macd_1m'] < dataframe['macdsignal_1m']) &
(dataframe['macd_1m'].shift(1) >= dataframe['macdsignal_1m'].shift(1))
)
return dataframe
def identify_same_level_segments(self, dataframe: DataFrame) -> DataFrame:
"""
识别同级别走势段(a+A结构)
实现5分钟级别的同级别分解:
1. 识别A0(即a)、A1、A2、A3等走势段
2. 判断每个段的类型(上涨/下跌):如果a+A向上,则Ai当i为奇数时向下,i为偶数时向上
3. 识别中枢形成条件
4. 计算盘整背驰(Ai与Ai+2比较力度)
5. 标记买卖点
"""
df = dataframe.copy()
# 初始化列
df['ai_index'] = -1 # Ai的索引(A0, A1, A2, ...
df['ai_type'] = 0 # 1: 上涨, -1: 下跌
df['ai_high'] = np.nan # Ai的高点
df['ai_low'] = np.nan # Ai的低点
df['ai_macd_max'] = np.nan # Ai的MACD最大值
df['ai_macd_min'] = np.nan # Ai的MACD最小值
df['zs_formed'] = False # 是否形成中枢
df['panzheng_beichi'] = False # 盘整背驰信号
df['buy_signal'] = False # 买入信号
df['sell_signal'] = False # 卖出信号
# 识别关键转折点(局部高点和低点)
window = 3 # 确认窗口
lookback = window + 1
# 高点识别(延迟确认)
df['temp_high'] = df['high'].shift(window)
df['is_pivot_high'] = (
(df['temp_high'] == df['temp_high'].rolling(window=lookback).max()) &
(df['temp_high'].notna())
)
# 低点识别(延迟确认)
df['temp_low'] = df['low'].shift(window)
df['is_pivot_low'] = (
(df['temp_low'] == df['temp_low'].rolling(window=lookback).min()) &
(df['temp_low'].notna())
)
# 逐行处理,识别走势段
ai_list = [] # 存储Ai段的信息:[(start_idx, end_idx, type, high, low, macd_max, macd_min), ...]
current_ai_start = None
current_ai_type = None # 1: 上涨, -1: 下跌
last_pivot_idx = None
last_pivot_type = None # 'high' or 'low'
for i in range(window, len(df)):
# 检查是否有新的转折点
is_new_pivot = False
pivot_type = None
if df.iloc[i]['is_pivot_high']:
is_new_pivot = True
pivot_type = 'high'
elif df.iloc[i]['is_pivot_low']:
is_new_pivot = True
pivot_type = 'low'
if is_new_pivot and last_pivot_idx is not None:
# 完成一个走势段
if current_ai_start is not None:
seg_df = df.iloc[current_ai_start:i]
if len(seg_df) >= 3: # 至少3根K线
# 使用已确认的数据计算(不包括当前转折点)
# 为了安全,只使用到 last_pivot_idx 之前的数据
confirmed_seg_df = df.iloc[current_ai_start:last_pivot_idx] if last_pivot_idx > current_ai_start else seg_df
if len(confirmed_seg_df) > 0:
high_val = confirmed_seg_df['high'].max()
low_val = confirmed_seg_df['low'].min()
macd_max = confirmed_seg_df['macd'].max()
macd_min = confirmed_seg_df['macd'].min()
else:
high_val = seg_df['high'].max()
low_val = seg_df['low'].min()
macd_max = seg_df['macd'].max()
macd_min = seg_df['macd'].min()
# 判断走势类型
if current_ai_type is None:
# 第一个段(A0),根据价格变化判断
if high_val > df.iloc[current_ai_start]['close']:
current_ai_type = 1 # 上涨
else:
current_ai_type = -1 # 下跌
else:
# 后续段:如果a+A向上,则Ai当i为奇数时向下,i为偶数时向上
# 简化处理:根据转折点类型判断
if pivot_type == 'high' and last_pivot_type == 'low':
current_ai_type = 1 # 上涨段
elif pivot_type == 'low' and last_pivot_type == 'high':
current_ai_type = -1 # 下跌段
ai_list.append({
'start': current_ai_start,
'end': i,
'type': current_ai_type,
'high': high_val,
'low': low_val,
'macd_max': macd_max,
'macd_min': macd_min
})
# 标记到dataframe(只在段结束时标记,避免未来数据)
# 使用滚动窗口:只在确认转折点后才标记前一段的信息
# 为了安全,只在段的最后几根K线标记(确认段已结束)
confirm_window = min(3, i - current_ai_start) # 确认窗口,最多3根K线
mark_start = max(current_ai_start, i - confirm_window)
df.iloc[mark_start:i, df.columns.get_loc('ai_index')] = len(ai_list) - 1
df.iloc[mark_start:i, df.columns.get_loc('ai_type')] = current_ai_type
# 高点和低点使用已确认的数据
df.iloc[mark_start:i, df.columns.get_loc('ai_high')] = high_val
df.iloc[mark_start:i, df.columns.get_loc('ai_low')] = low_val
df.iloc[mark_start:i, df.columns.get_loc('ai_macd_max')] = macd_max
df.iloc[mark_start:i, df.columns.get_loc('ai_macd_min')] = macd_min
# 开始新的走势段
current_ai_start = last_pivot_idx
last_pivot_idx = i
last_pivot_type = pivot_type
elif is_new_pivot:
# 第一个转折点
last_pivot_idx = i
last_pivot_type = pivot_type
if current_ai_start is None:
current_ai_start = 0
# 处理最后一个段
if current_ai_start is not None:
# 标记当前未完成的段
if len(df) - current_ai_start >= 3:
seg_df = df.iloc[current_ai_start:]
high_val = seg_df['high'].max()
low_val = seg_df['low'].min()
macd_max = seg_df['macd'].max()
macd_min = seg_df['macd'].min()
# 使用最后一个段的类型
if len(ai_list) > 0:
last_type = ai_list[-1]['type']
# 如果上一个段是上涨,当前应该是下跌(或相反)
current_ai_type = -last_type
else:
current_ai_type = 1 if high_val > df.iloc[current_ai_start]['close'] else -1
ai_list.append({
'start': current_ai_start,
'end': len(df),
'type': current_ai_type,
'high': high_val,
'low': low_val,
'macd_max': macd_max,
'macd_min': macd_min
})
df.iloc[current_ai_start:, df.columns.get_loc('ai_index')] = len(ai_list) - 1
df.iloc[current_ai_start:, df.columns.get_loc('ai_type')] = current_ai_type
df.iloc[current_ai_start:, df.columns.get_loc('ai_high')] = high_val
df.iloc[current_ai_start:, df.columns.get_loc('ai_low')] = low_val
df.iloc[current_ai_start:, df.columns.get_loc('ai_macd_max')] = macd_max
df.iloc[current_ai_start:, df.columns.get_loc('ai_macd_min')] = macd_min
# 识别中枢和盘整背驰
df = self.identify_zs_and_beichi(df, ai_list)
# 清理临时列
df = df.drop(columns=['temp_high', 'temp_low', 'is_pivot_high', 'is_pivot_low'])
return df
def identify_zs_and_beichi(self, dataframe: DataFrame, ai_list: list) -> DataFrame:
"""
识别中枢和盘整背驰
1. 中枢形成:如果A3跌破a的高点,则A1、A2、A3必然构成30分钟中枢
2. 盘整背驰:Ai与Ai+2之间比较力度(MACD面积或幅度)
3. 标记买卖点:
- 盘整背驰:i+2为偶数时卖出,i+2为奇数时买入
- 非背驰情况:根据Ai+3是否跌破/升破Ai的高低点决定
注意:为了避免未来数据,只在段确认结束后才标记信号
"""
df = dataframe.copy()
if len(ai_list) < 3:
return df
# 逐行处理,只在当前行可以确认历史段的信息时才标记
# 这样可以避免使用未来数据
for row_idx in range(len(df)):
# 找到当前行属于哪个段
current_ai_idx = -1
for ai_idx, ai in enumerate(ai_list):
if ai['start'] <= row_idx < ai['end']:
current_ai_idx = ai_idx
break
if current_ai_idx < 0:
continue
# 只在段的最后几根K线才处理,确保段已确认结束
current_ai = ai_list[current_ai_idx]
if row_idx < current_ai['end'] - 3: # 只在段的最后3根K线处理
continue
# 识别中枢(A1、A2、A3构成中枢)
# 只在A3段结束时才标记中枢,避免使用未来数据
if current_ai_idx >= 2: # 至少需要A0, A1, A2
a0 = ai_list[0]
a1 = ai_list[current_ai_idx - 2] if current_ai_idx >= 2 else None
a2 = ai_list[current_ai_idx - 1] if current_ai_idx >= 1 else None
a3 = ai_list[current_ai_idx]
if a1 and a2 and a3:
# 如果A3跌破a(A0)的高点,则A1、A2、A3构成中枢
if a3['low'] < a0['high']:
# 只在A3段的最后几根K线标记中枢
df.iloc[row_idx, df.columns.get_loc('zs_formed')] = True
# 盘整背驰判断:Ai与Ai+2比较力度
# 只在ai_plus_2段结束时才判断,避免使用未来数据
if current_ai_idx >= 2:
ai = ai_list[current_ai_idx - 2]
ai_plus_2 = ai_list[current_ai_idx]
# 计算力度(使用MACD面积或价格幅度)
if ai['type'] == ai_plus_2['type']: # 同方向才能比较
# 上涨段:比较MACD最大值和价格涨幅
if ai['type'] == 1: # 上涨
price_strength_ai = (ai['high'] - ai['low']) / ai['low'] if ai['low'] > 0 else 0
price_strength_ai2 = (ai_plus_2['high'] - ai_plus_2['low']) / ai_plus_2['low'] if ai_plus_2['low'] > 0 else 0
macd_strength_ai = ai['macd_max']
macd_strength_ai2 = ai_plus_2['macd_max']
# 盘整顶背驰:价格创新高或接近,但MACD力度减弱
beichi = (
(price_strength_ai2 <= price_strength_ai * 1.1) & # 价格涨幅相近或更小
(macd_strength_ai2 < macd_strength_ai * 0.9) # MACD力度明显减弱
)
else: # 下跌
price_strength_ai = (ai['high'] - ai['low']) / ai['low'] if ai['low'] > 0 else 0
price_strength_ai2 = (ai_plus_2['high'] - ai_plus_2['low']) / ai_plus_2['low'] if ai_plus_2['low'] > 0 else 0
macd_strength_ai = abs(ai['macd_min'])
macd_strength_ai2 = abs(ai_plus_2['macd_min'])
# 盘整底背驰:价格创新低或接近,但MACD力度减弱
beichi = (
(abs(price_strength_ai2) <= abs(price_strength_ai) * 1.1) & # 价格跌幅相近或更小
(macd_strength_ai2 < macd_strength_ai * 0.9) # MACD力度明显减弱
)
if beichi:
# 只在ai_plus_2段的最后几根K线标记信号
# i+2为偶数时卖出,i+2为奇数时买入
if (current_ai_idx) % 2 == 0: # 偶数,卖出
df.iloc[row_idx, df.columns.get_loc('sell_signal')] = True
df.iloc[row_idx, df.columns.get_loc('panzheng_beichi')] = True
else: # 奇数,买入
df.iloc[row_idx, df.columns.get_loc('buy_signal')] = True
df.iloc[row_idx, df.columns.get_loc('panzheng_beichi')] = True
# 非背驰情况的处理(简化版)
# 只在Ai+4段结束时才标记,避免使用未来数据
if current_ai_idx >= 4:
ai = ai_list[current_ai_idx - 4]
ai_plus_3 = ai_list[current_ai_idx - 1]
ai_plus_4 = ai_list[current_ai_idx]
if (current_ai_idx - 4) % 2 == 0: # i为偶数
# 若Ai+3不跌破Ai高点,继续持有(不标记卖出)
if ai_plus_3['low'] < ai['high']:
# Ai+3跌破Ai高点,在不创新高或盘整顶背驰的Ai+k+4卖出
if ai_plus_4['high'] <= ai_plus_3['high']: # 不创新高
df.iloc[row_idx, df.columns.get_loc('sell_signal')] = True
else: # i为奇数
# 若Ai+3不升破Ai低点,继续保持不回补
if ai_plus_3['high'] > ai['low']:
# Ai+3升破Ai低点,在不创新低或盘整底背驰的Ai+k+4回补
if ai_plus_4['low'] >= ai_plus_3['low']: # 不创新低
df.iloc[row_idx, df.columns.get_loc('buy_signal')] = True
return df
def detect_divergence(self, dataframe: DataFrame) -> DataFrame:
"""
检测背驰(使用滚动窗口,避免未来函数)
顶背驰:价格创新高,但MACD不创新高
底背驰:价格创新低,但MACD不创新低
"""
df = dataframe.copy()
# 使用滚动窗口检测背驰(只使用历史数据)
lookback = 20 # 向前看20根K线
# 顶背驰检测:当前价格是近期最高,但MACD不是近期最高
df['recent_high'] = df['high'].rolling(window=lookback).max()
df['recent_macd_max'] = df['macd'].rolling(window=lookback).max()
df['prev_recent_high'] = df['high'].rolling(window=lookback).max().shift(1)
df['prev_recent_macd_max'] = df['macd'].rolling(window=lookback).max().shift(1)
# 当前价格创新高,但MACD没有创新高(或降低)
df['divergence_top'] = (
(df['high'] >= df['recent_high']) & # 当前是近期最高
(df['high'] > df['prev_recent_high']) & # 比之前的最高更高
(df['macd'] < df['prev_recent_macd_max']) & # MACD没有创新高
(df['macd'] < 0) # MACD在零轴下方(下跌趋势中的顶背驰)
)
# 底背驰检测:当前价格是近期最低,但MACD不是近期最低
df['recent_low'] = df['low'].rolling(window=lookback).min()
df['recent_macd_min'] = df['macd'].rolling(window=lookback).min()
df['prev_recent_low'] = df['low'].rolling(window=lookback).min().shift(1)
df['prev_recent_macd_min'] = df['macd'].rolling(window=lookback).min().shift(1)
# 当前价格创新低,但MACD没有创新低(或升高)
df['divergence_bottom'] = (
(df['low'] <= df['recent_low']) & # 当前是近期最低
(df['low'] < df['prev_recent_low']) & # 比之前的最低更低
(df['macd'] > df['prev_recent_macd_min']) & # MACD没有创新低
(df['macd'] > 0) # MACD在零轴上方(上涨趋势中的底背驰)
)
return df
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
入场逻辑:基于同级别分解的a+A结构
1. 盘整背驰买入:i+2为奇数时的盘整背驰信号
2. 非背驰情况的买入:Ai+3升破Ai低点后的回补信号
"""
ticker = self.get_ticker_indicator()
resample_col = f"resample_{ticker * self.same_level_timeframe}_"
# 获取5m级别的指标
buy_signal_col = f"{resample_col}buy_signal"
panzheng_beichi_col = f"{resample_col}panzheng_beichi"
ai_type_col = f"{resample_col}ai_type"
rsi_5m_col = f"{resample_col}rsi"
# 获取30m级别的趋势指标
ema_trend_up_col = f"{resample_col}ema_trend_up"
ema_trend_dn_col = f"{resample_col}ema_trend_dn"
volatility_ok_col = f"{resample_col}volatility_ok"
strong_uptrend_col = f"{resample_col}strong_uptrend"
strong_downtrend_col = f"{resample_col}strong_downtrend"
price_above_ema200_col = f"{resample_col}price_above_ema200"
price_below_ema200_col = f"{resample_col}price_below_ema200"
# 做多条件(激进优化:在下跌趋势中禁止做多,只在强上涨趋势中做多)
# 1. 盘整背驰买入信号(i+2为奇数)
# 2. 非背驰情况的回补信号
# 3. 确认是上涨段或即将上涨
# 4. 强上涨趋势确认(必须价格在EMA200上方且EMA斜率向上)
# 5. 波动率确认
# 6. MACD确认
# 7. 禁止在下跌趋势中做多
dataframe.loc[
(
(dataframe[buy_signal_col] == True) & # 买入信号
(
(dataframe[panzheng_beichi_col] == True) | # 盘整背驰
(dataframe[ai_type_col] == 1) # 或当前是上涨段
) &
(dataframe[strong_uptrend_col] == True) & # 强上涨趋势(新增:必须强趋势)
(dataframe[price_above_ema200_col] == True) & # 价格在EMA200上方(新增)
(dataframe[volatility_ok_col] == True) & # 波动率足够
(dataframe[rsi_5m_col] < 60) & # RSI不过度超买(收紧)
(dataframe[rsi_5m_col] > 40) & # RSI在合理区间(收紧)
(dataframe['rsi_1m'] > 40) & # 1m RSI确认(收紧)
(dataframe['rsi_1m'] < 65) & # 1m RSI不过度超买(收紧)
(dataframe['macd_1m'] > dataframe['macdsignal_1m']) & # MACD向上
(dataframe['macd_1m'] > 0) & # MACD在零轴上方(新增)
(dataframe['volume'] > dataframe['volume_mean'] * 1.5) & # 成交量确认(提高阈值)
~(dataframe[strong_downtrend_col] == True) # 禁止在强下跌趋势中做多(新增)
),
["enter_long", "enter_tag"],
] = (1, "same_level_long")
# 做空条件(优化:收紧条件,提高质量)
# 1. 盘整背驰卖出信号(i+2为偶数,但这里作为做空入场)
# 2. 非背驰情况的卖出信号
# 3. 确认是下跌段或即将下跌
# 4. 强下跌趋势确认(必须价格在EMA200下方且EMA斜率向下)
# 5. 波动率确认
# 6. MACD确认
sell_signal_col = f"{resample_col}sell_signal"
dataframe.loc[
(
(dataframe[sell_signal_col] == True) & # 卖出信号
(
(dataframe[panzheng_beichi_col] == True) | # 盘整背驰(但i+2为偶数)
(dataframe[ai_type_col] == -1) # 或当前是下跌段
) &
(
(dataframe[strong_downtrend_col] == True) | # 强下跌趋势(优先)
(
(dataframe[ema_trend_dn_col] == True) & # 30m趋势向下
(dataframe[price_below_ema200_col] == True) # 且价格在EMA200下方
)
) &
(dataframe[volatility_ok_col] == True) & # 波动率足够
(dataframe[rsi_5m_col] > 40) & # RSI不过度超卖(收紧)
(dataframe[rsi_5m_col] < 65) & # RSI不过度超买(收紧)
(dataframe['rsi_1m'] < 65) & # 1m RSI确认(收紧)
(dataframe['rsi_1m'] > 35) & # 1m RSI不过度超卖(收紧)
(dataframe['macd_1m'] < dataframe['macdsignal_1m']) & # MACD向下
(dataframe['macd_1m'] < 0) & # MACD在零轴下方(新增)
(dataframe['volume'] > dataframe['volume_mean'] * 1.3) # 成交量确认(提高阈值)
),
["enter_short", "enter_tag"],
] = (1, "same_level_short")
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
出场逻辑:基于同级别分解的a+A结构
1. 盘整背驰卖出:i+2为偶数时的盘整背驰信号
2. 非背驰情况的卖出:Ai+3跌破Ai高点后的卖出信号
"""
ticker = self.get_ticker_indicator()
resample_col = f"resample_{ticker * self.same_level_timeframe}_"
# 获取5m级别的指标
sell_signal_col = f"{resample_col}sell_signal"
buy_signal_col = f"{resample_col}buy_signal"
panzheng_beichi_col = f"{resample_col}panzheng_beichi"
ai_type_col = f"{resample_col}ai_type"
rsi_5m_col = f"{resample_col}rsi"
ema_trend_up_col = f"{resample_col}ema_trend_up"
ema_trend_dn_col = f"{resample_col}ema_trend_dn"
strong_uptrend_col = f"{resample_col}strong_uptrend"
strong_downtrend_col = f"{resample_col}strong_downtrend"
# 做多出场(优化:更早退出,保护利润)
# 在趋势转弱或明确反转时退出
dataframe.loc[
(
(
(dataframe[sell_signal_col] == True) & # 明确的卖出信号
(dataframe[panzheng_beichi_col] == True) # 且是背驰信号
) |
(
(dataframe[ai_type_col] == -1) & # 转为下跌段
(dataframe[rsi_5m_col] > 55) & # RSI确认(降低阈值,更早退出)
(dataframe[ema_trend_dn_col] == True) # 且趋势确实向下
) |
(
(dataframe[strong_downtrend_col] == True) & # 强下跌趋势(新增)
(dataframe[rsi_5m_col] > 50) # RSI确认
)
),
["exit_long", "exit_tag"],
] = (1, "same_level_exit_long")
# 做空出场(优化:更早退出,保护利润)
# 在趋势转弱或明确反转时退出
dataframe.loc[
(
(
(dataframe[buy_signal_col] == True) & # 明确的买入信号
(dataframe[panzheng_beichi_col] == True) # 且是背驰信号
) |
(
(dataframe[ai_type_col] == 1) & # 转为上涨段
(dataframe[rsi_5m_col] < 45) & # RSI确认(提高阈值,更早退出)
(dataframe[ema_trend_up_col] == True) # 且趋势确实向上
) |
(
(dataframe[strong_uptrend_col] == True) & # 强上涨趋势(新增)
(dataframe[rsi_5m_col] < 50) # RSI确认
)
),
["exit_short", "exit_tag"],
] = (1, "same_level_exit_short")
return dataframe
def leverage(
self,
pair: str,
current_time: datetime,
current_rate: float,
proposed_leverage: float,
max_leverage: float,
entry_tag: Optional[str],
side: str,
**kwargs,
) -> float:
return 1.0
def get_ticker_indicator(self) -> int:
"""获取 timeframe 的分钟数"""
return int(self.timeframe[:-1])