添加新的策略
This commit is contained in:
@@ -0,0 +1,664 @@
|
||||
"""
|
||||
缠论同级别分解策略 (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])
|
||||
Reference in New Issue
Block a user