添加新的策略
This commit is contained in:
+2
-1
@@ -53,7 +53,7 @@ class ChanLun():
|
||||
self.time_M_symbols = ['2M', '3M', '6M', '1y']
|
||||
self.time_symbols = ['1m', '2m', '3m', '5m', '10m', '15m', '20m', '30m', '45m','1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d', '1w', '2w', '1M', '3M', '6M', '1y']
|
||||
self.tf_df_dict = {}
|
||||
self.ema_symbols = ['1m', '2m', '3m', '5m', '10m', '15m', '20m', '30m', '45m', '1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d']
|
||||
self.ema_symbols = ['5m', '10m', '15m', '20m', '30m', '45m', '1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d']
|
||||
self.tf_df = TF_DF()
|
||||
def init_data(self, dataframe, intervals, timeframes):
|
||||
for index in range(0, len(intervals)):
|
||||
@@ -61,6 +61,7 @@ class ChanLun():
|
||||
interval = intervals[index]
|
||||
self.tf_df_dict[timeframe] = TF_DF(dataframe, interval, timeframe)
|
||||
def init_dataframes(self, dataframe_m=None, dataframe_15m=None, dataframe_h=None, dataframe_d=None, dataframe_w=None, dataframe_M=None):
|
||||
self.tf_df_dict = {}
|
||||
if dataframe_m is not None:
|
||||
self.tf_df_dict['1m'] = TF_DF(dataframe_m, 1, '1m')
|
||||
self.init_data(dataframe_m, self.time_m_intervals, self.time_m_symbols)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"trading_mode": "futures",
|
||||
"margin_mode": "isolated",
|
||||
"can_short" : true,
|
||||
"timeframe" : "15m",
|
||||
"timeframe" : "1h",
|
||||
"process_only_new_candles" : false,
|
||||
"unfilledtimeout": {
|
||||
"entry": 1,
|
||||
|
||||
@@ -89,11 +89,15 @@ class ChanLun_EMA52(IStrategy):
|
||||
# startup_candle_count = 1600
|
||||
big_tf = '1h'
|
||||
small_tf = '15m'
|
||||
last_time = datetime.now()
|
||||
last_time = None
|
||||
chan = ChanLun()
|
||||
last_order = None
|
||||
last_trade = None
|
||||
pair = 'BTC/USDT:USDT'
|
||||
long_tf = '1h'
|
||||
short_tf = '15m'
|
||||
long_time = 60
|
||||
short_time = 15
|
||||
def informative_pairs(self):
|
||||
return [(self.pair, "1h"),
|
||||
(self.pair, "1d"),
|
||||
@@ -102,16 +106,31 @@ class ChanLun_EMA52(IStrategy):
|
||||
(self.pair, "1w"),
|
||||
]
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
|
||||
if self.last_time + timedelta(minutes=1) < datetime.now():
|
||||
long_df = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1h')
|
||||
long_df['entry_long'] = self.long_entry_condition(long_df)
|
||||
dataframe['rsi'] = ta.RSI(long_df, timeperiod=14)
|
||||
if self.last_time is None or self.last_time + timedelta(minutes=1) < datetime.now():
|
||||
self.last_time = datetime.now()
|
||||
logger.info("init_dataframes----------------------------")
|
||||
last_price = dataframe.iloc[-1]['close']
|
||||
macdstr = str(long_df.iloc[-1]['macd']) + " " + str(long_df.iloc[-1]['macdsignal']) + " " + str(long_df.iloc[-1]['macdhist']))
|
||||
date = dataframe.iloc[-1]['date']
|
||||
tf_ema52_list = self.chan.check_price_ema52(last_price)
|
||||
self.init_dataframes(dataframe)
|
||||
logger.info("Date: " + date.strftime('%Y-%m-%d %H:%M:%S') + " Price: " + str(last_price) + " EMA52_list: " + str(tf_ema52_list))
|
||||
logger.info("Date: " + date.strftime('%Y-%m-%d %H:%M:%S') + " Price: " + str(last_price) + " EMA52_list: " + str(tf_ema52_list) + " MACD: " + macdstr)
|
||||
return dataframe
|
||||
def long_entry_condition(self, long_df):
|
||||
long_df['ema52'] = ta.EMA(long_df, timeperiod=52)
|
||||
long_df['dir52'] = long_df['close'] - long_df['ema52']
|
||||
long_df['ema156'] = ta.EMA(long_df, timeperiod=156)
|
||||
long_df['dir156'] = long_df['close'] - long_df['ema156']
|
||||
long_df_macd = ta.MACD(long_df, fast=12, slow=26, signal=9)
|
||||
long_df['macdsignal'] = long_df_macd['macdsignal']
|
||||
long_df['macd'] = long_df_macd['macd']
|
||||
long_df['macdhist'] = long_df_macd['macdhist']
|
||||
long_entry_condition = (long_df['dir52'] > 0) & (long_df['dir156'] > 0) & (long_df['macdhist'] > 0)
|
||||
return long_entry_condition
|
||||
|
||||
def init_dataframes(self, dataframe_1m):
|
||||
dataframe_15m = self.dp.get_pair_dataframe(pair=self.pair, timeframe='15m')
|
||||
dataframe_1h = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1h')
|
||||
|
||||
+396
-74
@@ -1,115 +1,437 @@
|
||||
# --- Do not remove these libs ---
|
||||
from statistics import median
|
||||
from freqtrade.strategy import IStrategy
|
||||
from technical.util import resample_to_interval, resampled_merge
|
||||
from pandas import DataFrame
|
||||
import talib.abstract as ta
|
||||
from technical import qtpylib
|
||||
import numpy as np
|
||||
|
||||
# 1小时短线趋势追踪策略 V7 - EMA + MACD 综合趋势判断
|
||||
# 使用 EMA8, EMA24, EMA72, EMA168 + MACD 识别趋势阶段和强度
|
||||
# freqtrade backtesting -c ./user_data/Chan/config/EMA_Pattern.json --strategy EMA_Pattern --strategy-path ./user_data/Chan/strategies --timerange=20250101-
|
||||
# freqtrade download-data -c ./user_data/Chan/config/EMA_Pattern.json -t 1h --pairs BTC/USDT:USDT --timerange=20250101-
|
||||
|
||||
### Now you can use logger.info('asfd') to log
|
||||
# freqtrade plot-dataframe --strategy EMA_Pattern --datadir user_data/data/binance -c ./user_data/Chan/EMA_Pattern.json --timerange=20250309-
|
||||
# freqtrade backtesting -c ./user_data/Chan/config/EMA_Pattern.json --strategy EMA_Pattern --strategy-path ./user_data/Chan/strategies --timerange=20251030-
|
||||
# freqtrade download-data -c ./user_data/Chan/config/EMA_Pattern.json -t 1m 3m 5m 15m 30m 1h --pairs BTC/USDT:USDT --timerange=20250405-
|
||||
# freqtrade download-data -c ./user_data/Chan/config/EMA_Pattern.json -t 1m 1h 1d 1M --pairs BTC/USDT --timerange=20170101-
|
||||
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi --strategy EMA_Pattern --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/EMA_Pattern.json -e 200 --timerange=20250201-20250901
|
||||
# freqtrade edge -c ./user_data/Chan/config/EMA_Pattern.json --strategy EMA_Pattern --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901
|
||||
# freqtrade plot-dataframe -c ./user_data/Chan/config/EMA_Pattern.json --strategy EMA_Pattern --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901
|
||||
|
||||
class EMA_Pattern(IStrategy):
|
||||
time1h = 1440
|
||||
can_short: bool = True
|
||||
timeframe: str = "1m"
|
||||
process_only_new_candles: bool = False
|
||||
"""
|
||||
1小时K线短线趋势追踪策略 V7 - EMA + MACD
|
||||
|
||||
# ROI 与止损可根据需要在配置中覆盖
|
||||
核心逻辑:
|
||||
1. EMA多头排列 + MACD金叉确认 = 趋势启动(最佳入场)
|
||||
2. EMA发散 + MACD动能增加 = 趋势发展(可追涨)
|
||||
3. MACD动能减弱/顶背离 = 趋势成熟(减仓)
|
||||
4. EMA死叉 + MACD死叉 = 趋势结束(出场)
|
||||
"""
|
||||
|
||||
can_short: bool = True
|
||||
timeframe: str = "1h"
|
||||
process_only_new_candles: bool = True
|
||||
|
||||
# EMA参数
|
||||
ema_fast: int = 8
|
||||
ema_short: int = 24
|
||||
ema_mid: int = 72
|
||||
ema_long: int = 168
|
||||
|
||||
# 趋势强度阈值(放宽以增加交易机会)
|
||||
min_trend_strength: int = 65
|
||||
|
||||
# ADX阈值
|
||||
min_adx: int = 25 # 有趋势就交易
|
||||
|
||||
# ROI - V6最佳参数(无杠杆)
|
||||
minimal_roi = {
|
||||
"60": 0.005,
|
||||
"30": 0.01,
|
||||
"0": 0.02,
|
||||
"120": 0.02, # 5天后 2%止盈
|
||||
"72": 0.03, # 3天后 3%止盈
|
||||
"24": 0.05, # 1天后 5%止盈
|
||||
"0": 0.08, # 立即 8%止盈
|
||||
}
|
||||
stoploss: float = -0.30
|
||||
# 需要的历史K线数量(包含EMA等指标预热)
|
||||
startup_candle_count: int = 200
|
||||
|
||||
# 固定止损
|
||||
stoploss: float = -0.05 # 5%止损
|
||||
|
||||
# 追踪止损
|
||||
trailing_stop: bool = True
|
||||
trailing_stop_positive: float = 0.025 # 盈利2.5%后启动
|
||||
trailing_stop_positive_offset: float = 0.04 # 盈利4%后才触发
|
||||
trailing_only_offset_is_reached: bool = True
|
||||
|
||||
# 禁用自定义止损(让追踪止损工作)
|
||||
use_custom_stoploss: bool = False
|
||||
|
||||
# 只做多(做空效果差)
|
||||
can_short: bool = False
|
||||
|
||||
startup_candle_count: int = 250
|
||||
|
||||
def custom_stoploss(self, pair: str, trade, current_time, current_rate,
|
||||
current_profit, after_fill, **kwargs) -> float:
|
||||
"""
|
||||
动态止损 V3 - 保护利润但不过早出场:
|
||||
1. 盈利超过6%,止损移到盈利3%(锁定一半利润)
|
||||
2. 盈利超过3%,止损移到盈利1%
|
||||
3. 盈利超过1.5%,止损移到保本
|
||||
4. 长时间亏损才考虑缩紧止损
|
||||
"""
|
||||
# 持仓时间(小时)
|
||||
trade_duration = (current_time - trade.open_date_utc).total_seconds() / 3600
|
||||
|
||||
# 盈利时动态止损 - 阶梯式保护利润
|
||||
if current_profit > 0.06:
|
||||
# 盈利超过6%,锁定3%利润
|
||||
return -0.03
|
||||
elif current_profit > 0.03:
|
||||
# 盈利超过3%,锁定1%利润
|
||||
return -0.02
|
||||
elif current_profit > 0.015:
|
||||
# 盈利超过1.5%,移到保本
|
||||
return -0.005
|
||||
|
||||
# 长时间持仓亏损(超过96小时=4天),才缩紧止损
|
||||
if trade_duration > 96 and current_profit < -0.03:
|
||||
return -0.04 # 缩紧到4%
|
||||
|
||||
# 默认使用配置的止损
|
||||
return self.stoploss
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
if dataframe is None or dataframe.empty:
|
||||
return dataframe
|
||||
dataframe = self.add_indicators(dataframe)
|
||||
return dataframe
|
||||
def add_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||
macd = ta.MACD(dataframe, timeperiod=12, fastperiod=12, slowperiod=26, signalperiod=9)
|
||||
|
||||
# ==================== EMA指标 ====================
|
||||
dataframe['ema8'] = ta.EMA(dataframe, timeperiod=self.ema_fast)
|
||||
dataframe['ema24'] = ta.EMA(dataframe, timeperiod=self.ema_short)
|
||||
dataframe['ema72'] = ta.EMA(dataframe, timeperiod=self.ema_mid)
|
||||
dataframe['ema168'] = ta.EMA(dataframe, timeperiod=self.ema_long)
|
||||
|
||||
# ==================== MACD指标 ====================
|
||||
macd = ta.MACD(dataframe, fastperiod=12, slowperiod=26, signalperiod=9)
|
||||
dataframe['macd'] = macd['macd']
|
||||
dataframe['macdsignal'] = macd['macdsignal']
|
||||
dataframe['macdhist'] = macd['macdhist']
|
||||
dataframe['ema6'] = ta.EMA(dataframe, timeperiod=6)
|
||||
dataframe['ema12'] = ta.EMA(dataframe, timeperiod=12)
|
||||
dataframe['ema24'] = ta.EMA(dataframe, timeperiod=24)
|
||||
dataframe['ema52'] = ta.EMA(dataframe, timeperiod=52)
|
||||
dataframe['macd_signal'] = macd['macdsignal']
|
||||
dataframe['macd_hist'] = macd['macdhist']
|
||||
|
||||
# MACD辅助指标
|
||||
dataframe['macd_hist_change'] = dataframe['macd_hist'] - dataframe['macd_hist'].shift(1)
|
||||
dataframe['macd_hist_ma'] = dataframe['macd_hist'].rolling(5).mean()
|
||||
|
||||
# ADX趋势强度
|
||||
dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)
|
||||
dataframe['strong_trend'] = dataframe['adx'] > 25
|
||||
dataframe['UP_Pattern'] = (dataframe['ema6'] > dataframe['ema12']) & (dataframe['ema12'] > dataframe['ema24']) & (dataframe['ema24'] > dataframe['ema52'])
|
||||
dataframe['DOWN_Pattern'] = (dataframe['ema6'] < dataframe['ema12']) & (dataframe['ema12'] < dataframe['ema24']) & (dataframe['ema24'] < dataframe['ema52'])
|
||||
dataframe['UP_Confirm'] = (dataframe['ema6'] > dataframe['ema6'].shift(1)) & (dataframe['ema12'] > dataframe['ema12'].shift(1)) & (dataframe['ema24'] > dataframe['ema24'].shift(1)) & (dataframe['ema52'] > dataframe['ema52'].shift(1))
|
||||
dataframe['DOWN_Confirm'] = (dataframe['ema6'] < dataframe['ema6'].shift(1)) & (dataframe['ema12'] < dataframe['ema12'].shift(1)) & (dataframe['ema24'] < dataframe['ema24'].shift(1)) & (dataframe['ema52'] < dataframe['ema52'].shift(1))
|
||||
dataframe['EMA52_Cross_EMA24_UP'] = (dataframe['ema52'] < dataframe['ema24']) & (dataframe['ema52'].shift(1) > dataframe['ema24'].shift(1))
|
||||
dataframe['EMA52_Cross_EMA24_DOWN'] = (dataframe['ema52'] > dataframe['ema24']) & (dataframe['ema52'].shift(1) < dataframe['ema24'].shift(1))
|
||||
dataframe['Price_Above_EMA52'] = (dataframe['close'] > dataframe['ema52'])
|
||||
dataframe['Price_Below_EMA52'] = (dataframe['close'] < dataframe['ema52'])
|
||||
dataframe['MACD_Above_Zero'] = (dataframe['macd'] > 0) & (dataframe['macdsignal'] > 0)
|
||||
dataframe['MACD_Below_Zero'] = (dataframe['macd'] < 0) & (dataframe['macdsignal'] < 0)
|
||||
dataframe['BUY_END'] = dataframe['close'] < dataframe['ema52']
|
||||
dataframe['SELL_END'] = dataframe['close'] > dataframe['ema52']
|
||||
|
||||
# ATR波动率
|
||||
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
|
||||
dataframe['atr_pct'] = dataframe['atr'] / dataframe['close'] * 100
|
||||
|
||||
# 成交量
|
||||
dataframe['volume_ma'] = dataframe['volume'].rolling(20).mean()
|
||||
dataframe['volume_ratio'] = dataframe['volume'] / dataframe['volume_ma']
|
||||
|
||||
# ==================== 均线排列 ====================
|
||||
dataframe['bull_align'] = (
|
||||
(dataframe['ema8'] > dataframe['ema24']) &
|
||||
(dataframe['ema24'] > dataframe['ema72']) &
|
||||
(dataframe['ema72'] > dataframe['ema168'])
|
||||
)
|
||||
|
||||
dataframe['bear_align'] = (
|
||||
(dataframe['ema8'] < dataframe['ema24']) &
|
||||
(dataframe['ema24'] < dataframe['ema72']) &
|
||||
(dataframe['ema72'] < dataframe['ema168'])
|
||||
)
|
||||
|
||||
# 简化排列(短中期)
|
||||
dataframe['bull_align_short'] = (
|
||||
(dataframe['ema8'] > dataframe['ema24']) &
|
||||
(dataframe['ema24'] > dataframe['ema72'])
|
||||
)
|
||||
|
||||
dataframe['bear_align_short'] = (
|
||||
(dataframe['ema8'] < dataframe['ema24']) &
|
||||
(dataframe['ema24'] < dataframe['ema72'])
|
||||
)
|
||||
|
||||
# ==================== 均线斜率 ====================
|
||||
lookback = 5
|
||||
dataframe['slope_ema8'] = (dataframe['ema8'] - dataframe['ema8'].shift(lookback)) / dataframe['ema8'].shift(lookback) * 100
|
||||
dataframe['slope_ema24'] = (dataframe['ema24'] - dataframe['ema24'].shift(lookback)) / dataframe['ema24'].shift(lookback) * 100
|
||||
dataframe['slope_ema72'] = (dataframe['ema72'] - dataframe['ema72'].shift(lookback)) / dataframe['ema72'].shift(lookback) * 100
|
||||
dataframe['slope_ema168'] = (dataframe['ema168'] - dataframe['ema168'].shift(lookback)) / dataframe['ema168'].shift(lookback) * 100
|
||||
|
||||
dataframe['slope_bull_confirm'] = (
|
||||
(dataframe['slope_ema8'] > 0) &
|
||||
(dataframe['slope_ema24'] > 0) &
|
||||
(dataframe['slope_ema72'] > 0)
|
||||
)
|
||||
|
||||
dataframe['slope_bear_confirm'] = (
|
||||
(dataframe['slope_ema8'] < 0) &
|
||||
(dataframe['slope_ema24'] < 0) &
|
||||
(dataframe['slope_ema72'] < 0)
|
||||
)
|
||||
|
||||
# ==================== 均线间距 ====================
|
||||
dataframe['spread_total'] = (dataframe['ema8'] - dataframe['ema168']) / dataframe['ema168'] * 100
|
||||
dataframe['spread_short'] = (dataframe['ema8'] - dataframe['ema24']) / dataframe['ema24'] * 100
|
||||
dataframe['spread_change'] = dataframe['spread_total'] - dataframe['spread_total'].shift(lookback)
|
||||
dataframe['spread_ma'] = dataframe['spread_total'].rolling(20).mean()
|
||||
|
||||
spread_abs = dataframe['spread_total'].abs()
|
||||
dataframe['spread_pct_30'] = spread_abs.rolling(50, min_periods=20).quantile(0.3)
|
||||
dataframe['spread_pct_70'] = spread_abs.rolling(50, min_periods=20).quantile(0.7)
|
||||
|
||||
# ==================== MACD交叉信号 ====================
|
||||
# MACD金叉(MACD线上穿信号线)
|
||||
dataframe['macd_golden_cross'] = (
|
||||
(dataframe['macd'] > dataframe['macd_signal']) &
|
||||
(dataframe['macd'].shift(1) <= dataframe['macd_signal'].shift(1))
|
||||
)
|
||||
|
||||
# MACD死叉
|
||||
dataframe['macd_death_cross'] = (
|
||||
(dataframe['macd'] < dataframe['macd_signal']) &
|
||||
(dataframe['macd'].shift(1) >= dataframe['macd_signal'].shift(1))
|
||||
)
|
||||
|
||||
# MACD零轴上方金叉(更强信号)
|
||||
dataframe['macd_strong_golden'] = dataframe['macd_golden_cross'] & (dataframe['macd'] > 0)
|
||||
|
||||
# ==================== MACD动能判断 ====================
|
||||
# 柱状图动能增加(多头)
|
||||
dataframe['macd_momentum_up'] = (
|
||||
(dataframe['macd_hist'] > 0) &
|
||||
(dataframe['macd_hist'] > dataframe['macd_hist'].shift(1)) &
|
||||
(dataframe['macd_hist'].shift(1) > dataframe['macd_hist'].shift(2))
|
||||
)
|
||||
|
||||
# 柱状图动能增加(空头)
|
||||
dataframe['macd_momentum_down'] = (
|
||||
(dataframe['macd_hist'] < 0) &
|
||||
(dataframe['macd_hist'] < dataframe['macd_hist'].shift(1)) &
|
||||
(dataframe['macd_hist'].shift(1) < dataframe['macd_hist'].shift(2))
|
||||
)
|
||||
|
||||
# 柱状图动能减弱(顶部信号)
|
||||
dataframe['macd_momentum_weakening'] = (
|
||||
(dataframe['macd_hist'] > 0) &
|
||||
(dataframe['macd_hist'] < dataframe['macd_hist'].shift(1)) &
|
||||
(dataframe['macd_hist'].shift(1) < dataframe['macd_hist'].shift(2))
|
||||
)
|
||||
|
||||
# ==================== 趋势阶段(EMA + MACD综合判断)====================
|
||||
# 趋势启动期:EMA开始排列 + MACD金叉 + 柱状图正向增长
|
||||
dataframe['trend_start_bull'] = (
|
||||
dataframe['bull_align_short'] & # 至少短中期排列
|
||||
(dataframe['macd'] > dataframe['macd_signal']) & # MACD金叉状态
|
||||
(dataframe['macd_hist'] > 0) & # 柱状图为正
|
||||
(dataframe['macd_hist'] > dataframe['macd_hist'].shift(1)) & # 动能增加
|
||||
(spread_abs < dataframe['spread_pct_70']) # 发散度不是最大
|
||||
)
|
||||
|
||||
# 趋势发展期:完整排列 + MACD在零轴上方 + 持续放量
|
||||
dataframe['trend_develop_bull'] = (
|
||||
dataframe['bull_align'] & # 完整4线排列
|
||||
(dataframe['macd'] > 0) & # MACD在零轴上方
|
||||
(dataframe['macd'] > dataframe['macd_signal']) & # 金叉状态
|
||||
dataframe['slope_bull_confirm'] & # 斜率确认
|
||||
(dataframe['spread_change'] > 0) # 发散度扩大
|
||||
)
|
||||
|
||||
# 趋势成熟期:发散度大 + MACD动能减弱
|
||||
dataframe['trend_mature_bull'] = (
|
||||
dataframe['bull_align'] &
|
||||
(dataframe['macd'] > 0) &
|
||||
dataframe['macd_momentum_weakening'] & # 动能减弱
|
||||
(spread_abs > dataframe['spread_pct_70']) # 发散度很大
|
||||
)
|
||||
|
||||
# 趋势结束:EMA开始死叉 + MACD死叉
|
||||
dataframe['trend_end_bull'] = (
|
||||
(dataframe['ema8'] < dataframe['ema24']) & # EMA8死叉EMA24
|
||||
(dataframe['macd'] < dataframe['macd_signal']) & # MACD死叉
|
||||
(dataframe['macd_hist'] < 0) # 柱状图转负
|
||||
)
|
||||
|
||||
# 空头阶段(镜像)
|
||||
dataframe['trend_start_bear'] = (
|
||||
dataframe['bear_align_short'] &
|
||||
(dataframe['macd'] < dataframe['macd_signal']) &
|
||||
(dataframe['macd_hist'] < 0) &
|
||||
(dataframe['macd_hist'] < dataframe['macd_hist'].shift(1)) &
|
||||
(spread_abs < dataframe['spread_pct_70'])
|
||||
)
|
||||
|
||||
dataframe['trend_develop_bear'] = (
|
||||
dataframe['bear_align'] &
|
||||
(dataframe['macd'] < 0) &
|
||||
(dataframe['macd'] < dataframe['macd_signal']) &
|
||||
dataframe['slope_bear_confirm'] &
|
||||
(dataframe['spread_change'] < 0)
|
||||
)
|
||||
|
||||
dataframe['trend_end_bear'] = (
|
||||
(dataframe['ema8'] > dataframe['ema24']) &
|
||||
(dataframe['macd'] > dataframe['macd_signal']) &
|
||||
(dataframe['macd_hist'] > 0)
|
||||
)
|
||||
|
||||
# ==================== 趋势强度计算 (0-100) ====================
|
||||
# 1. 排列得分 (0-25)
|
||||
bull_align_score = (
|
||||
(dataframe['ema8'] > dataframe['ema24']).astype(int) +
|
||||
(dataframe['ema24'] > dataframe['ema72']).astype(int) +
|
||||
(dataframe['ema72'] > dataframe['ema168']).astype(int) +
|
||||
(dataframe['close'] > dataframe['ema8']).astype(int)
|
||||
) * 6.25
|
||||
|
||||
bear_align_score = (
|
||||
(dataframe['ema8'] < dataframe['ema24']).astype(int) +
|
||||
(dataframe['ema24'] < dataframe['ema72']).astype(int) +
|
||||
(dataframe['ema72'] < dataframe['ema168']).astype(int) +
|
||||
(dataframe['close'] < dataframe['ema8']).astype(int)
|
||||
) * 6.25
|
||||
|
||||
dataframe['align_score'] = np.maximum(bull_align_score, bear_align_score)
|
||||
|
||||
# 2. 斜率得分 (0-25)
|
||||
dataframe['slope_score'] = np.clip(np.abs(dataframe['slope_ema24']) * 10, 0, 25)
|
||||
|
||||
# 3. MACD动能得分 (0-25) - 替换原来的间距得分
|
||||
macd_hist_norm = dataframe['macd_hist'].abs() / dataframe['close'] * 1000
|
||||
dataframe['macd_score'] = np.clip(macd_hist_norm * 5, 0, 25)
|
||||
|
||||
# 4. ADX得分 (0-25)
|
||||
dataframe['adx_score'] = np.clip(dataframe['adx'] - 15, 0, 25)
|
||||
|
||||
# 综合强度
|
||||
dataframe['trend_strength'] = (
|
||||
dataframe['align_score'] +
|
||||
dataframe['slope_score'] +
|
||||
dataframe['macd_score'].fillna(12.5) +
|
||||
dataframe['adx_score']
|
||||
)
|
||||
|
||||
# ==================== 入场信号 ====================
|
||||
# EMA金叉
|
||||
dataframe['ema8_cross_ema24_up'] = (
|
||||
(dataframe['ema8'] > dataframe['ema24']) &
|
||||
(dataframe['ema8'].shift(1) <= dataframe['ema24'].shift(1))
|
||||
)
|
||||
|
||||
dataframe['ema8_cross_ema24_down'] = (
|
||||
(dataframe['ema8'] < dataframe['ema24']) &
|
||||
(dataframe['ema8'].shift(1) >= dataframe['ema24'].shift(1))
|
||||
)
|
||||
|
||||
# 回踩EMA24反弹
|
||||
dataframe['pullback_buy'] = (
|
||||
(dataframe['low'].shift(1) <= dataframe['ema24'].shift(1) * 1.005) &
|
||||
(dataframe['close'] > dataframe['ema8']) &
|
||||
(dataframe['close'] > dataframe['open']) &
|
||||
(dataframe['macd'] > dataframe['macd_signal']) # MACD确认
|
||||
)
|
||||
|
||||
# 突破前高
|
||||
dataframe['high_break'] = dataframe['close'] > dataframe['high'].rolling(24).max().shift(1)
|
||||
dataframe['low_break'] = dataframe['close'] < dataframe['low'].rolling(24).min().shift(1)
|
||||
|
||||
# ==================== 大周期趋势过滤 ====================
|
||||
dataframe['big_trend_bull'] = (
|
||||
(dataframe['ema168'] > dataframe['ema168'].shift(24)) &
|
||||
(dataframe['close'] > dataframe['ema168'])
|
||||
)
|
||||
|
||||
dataframe['big_trend_bear'] = (
|
||||
(dataframe['ema168'] < dataframe['ema168'].shift(24)) &
|
||||
(dataframe['close'] < dataframe['ema168'])
|
||||
)
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
if dataframe is None or dataframe.empty:
|
||||
return dataframe
|
||||
|
||||
# ==================== 做多入场1:趋势启动(EMA排列 + MACD确认)====================
|
||||
# 核心条件:EMA开始多头排列 + MACD金叉 + 动能增加
|
||||
dataframe.loc[
|
||||
(
|
||||
(dataframe['UP_Pattern']) &
|
||||
(dataframe['UP_Confirm']) &
|
||||
(dataframe['Price_Above_EMA52']) &
|
||||
(dataframe['MACD_Above_Zero']) &
|
||||
(dataframe['EMA52_Cross_EMA24_UP']) &
|
||||
(dataframe['strong_trend'])
|
||||
dataframe['bull_align_short'] & # 至少短中期排列(EMA8>24>72)
|
||||
(dataframe['macd'] > dataframe['macd_signal']) & # MACD金叉
|
||||
(dataframe['macd_hist'] > 0) & # 柱状图为正
|
||||
(dataframe['macd_hist'] > dataframe['macd_hist'].shift(1)) & # 动能增加
|
||||
(dataframe['close'] > dataframe['ema24']) & # 价格在EMA24上方
|
||||
(dataframe['adx'] > 20) & # 有趋势
|
||||
(dataframe['slope_ema8'] > 0) & # EMA8向上
|
||||
(dataframe['volume_ratio'] > 0.7)
|
||||
),
|
||||
["enter_long", "enter_tag"],
|
||||
] = (1, "ema_up_trend")
|
||||
] = (1, "trend_start")
|
||||
|
||||
# ==================== 做多入场2:强势突破(突破前高 + MACD确认)====================
|
||||
dataframe.loc[
|
||||
(
|
||||
(dataframe['DOWN_Pattern']) &
|
||||
(dataframe['DOWN_Confirm']) &
|
||||
(dataframe['Price_Below_EMA52']) &
|
||||
(dataframe['MACD_Below_Zero']) &
|
||||
(dataframe['EMA52_Cross_EMA24_DOWN']) &
|
||||
(dataframe['strong_trend'])
|
||||
dataframe['bull_align'] & # 完整排列
|
||||
dataframe['high_break'] & # 突破前高
|
||||
(dataframe['macd'] > dataframe['macd_signal']) & # MACD金叉状态
|
||||
(dataframe['adx'] > self.min_adx) & # 强趋势
|
||||
(dataframe['trend_strength'] >= self.min_trend_strength - 10) &
|
||||
(dataframe['volume_ratio'] > 0.9)
|
||||
),
|
||||
["enter_short", "enter_tag"],
|
||||
] = (1, "ema_down_trend")
|
||||
["enter_long", "enter_tag"],
|
||||
] = (1, "bull_breakout")
|
||||
|
||||
# ==================== 做多入场3:回踩反弹(趋势中回调)====================
|
||||
dataframe.loc[
|
||||
(
|
||||
dataframe['bull_align'] & # 完整排列
|
||||
(dataframe['low'].shift(1) <= dataframe['ema24'].shift(1) * 1.01) & # 回踩EMA24附近
|
||||
(dataframe['close'] > dataframe['open']) & # 阳线
|
||||
(dataframe['close'] > dataframe['ema8']) & # 收盘价回到EMA8上方
|
||||
(dataframe['macd'] > 0) & # MACD在零轴上方
|
||||
(dataframe['macd'] > dataframe['macd_signal']) & # 金叉状态
|
||||
(dataframe['trend_strength'] >= self.min_trend_strength - 15)
|
||||
),
|
||||
["enter_long", "enter_tag"],
|
||||
] = (1, "bull_pullback")
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
if dataframe is None or dataframe.empty:
|
||||
return dataframe
|
||||
dataframe.loc[
|
||||
(
|
||||
(dataframe['BUY_END']) |
|
||||
(dataframe['DOWN_Pattern']) |
|
||||
(dataframe['EMA52_Cross_EMA24_DOWN'])
|
||||
),
|
||||
["exit_long", "exit_tag"],
|
||||
] = (1, "ema_long_exit")
|
||||
|
||||
# ==================== 多头出场1:趋势结束(EMA死叉 + MACD死叉)====================
|
||||
dataframe.loc[
|
||||
(
|
||||
(dataframe['SELL_END']) |
|
||||
(dataframe['UP_Pattern']) |
|
||||
(dataframe['EMA52_Cross_EMA24_UP'])
|
||||
),
|
||||
dataframe['trend_end_bull'] & # EMA8死叉 + MACD死叉 + 柱状图转负
|
||||
(dataframe['close'] < dataframe['ema24']), # 价格确认跌破EMA24
|
||||
["exit_long", "exit_tag"],
|
||||
] = (1, "trend_end")
|
||||
|
||||
# ==================== 多头出场2:趋势反转(完全转为空头)====================
|
||||
dataframe.loc[
|
||||
dataframe['bear_align'] & # 完全空头排列
|
||||
(dataframe['macd'] < dataframe['macd_signal']) & # MACD死叉
|
||||
(dataframe['macd_hist'] < 0) & # 柱状图为负
|
||||
(dataframe['close'] < dataframe['ema72']) & # 价格跌破EMA72
|
||||
(dataframe['close'].shift(1) < dataframe['ema72'].shift(1)), # 连续2根确认
|
||||
["exit_long", "exit_tag"],
|
||||
] = (1, "trend_reverse")
|
||||
|
||||
# ==================== 空头出场1:趋势结束 ====================
|
||||
dataframe.loc[
|
||||
dataframe['trend_end_bear'] &
|
||||
(dataframe['close'] > dataframe['ema24']),
|
||||
["exit_short", "exit_tag"],
|
||||
] = (1, "ema_short_exit")
|
||||
] = (1, "trend_end")
|
||||
|
||||
# ==================== 空头出场2:趋势反转 ====================
|
||||
dataframe.loc[
|
||||
dataframe['bull_align'] &
|
||||
(dataframe['macd'] > dataframe['macd_signal']) &
|
||||
(dataframe['macd_hist'] > 0) &
|
||||
(dataframe['close'] > dataframe['ema72']) &
|
||||
(dataframe['close'].shift(1) > dataframe['ema72'].shift(1)),
|
||||
["exit_short", "exit_tag"],
|
||||
] = (1, "trend_reverse")
|
||||
|
||||
return dataframe
|
||||
|
||||
def get_ticker_indicator(self):
|
||||
return int(self.timeframe[:-1])
|
||||
Reference in New Issue
Block a user