This commit is contained in:
parent
040c5b6c36
commit
cfda8173a0
@ -243,3 +243,8 @@ plot_config = {
|
||||
|
||||
如果你希望我帮你生成完整的策略文件,并提供对应的 `config.json` 示例,请告诉我 👇 我可以一步步帮你完成。
|
||||
|
||||
freqtrade plot-dataframe --strategy DivergenceRegressionStrategy \
|
||||
--timerange=20250501-20250510 \
|
||||
--timeframe=5m \
|
||||
--exchange=binance \
|
||||
--pair=ETH/USDT
|
||||
|
||||
28
freqtrade/templates/freqaiprimer.json
Normal file
28
freqtrade/templates/freqaiprimer.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"strategy_name": "FreqaiPrimer",
|
||||
"params": {
|
||||
"max_open_trades": {
|
||||
"max_open_trades": 2
|
||||
},
|
||||
"buy": {},
|
||||
"sell": {},
|
||||
"protection": {
|
||||
"cooldown_period": 5
|
||||
},
|
||||
"roi": {
|
||||
"0": 0.02,
|
||||
"30": 0.01,
|
||||
"60": 0
|
||||
},
|
||||
"stoploss": {
|
||||
"stoploss": -0.015
|
||||
},
|
||||
"trailing": {
|
||||
"trailing_stop": true,
|
||||
"trailing_stop_positive": 0.005,
|
||||
"trailing_stop_positive_offset": 0.01
|
||||
}
|
||||
},
|
||||
"ft_stratparam_v": 1,
|
||||
"export_time": "2025-05-31 23:03:00.000000+09:00"
|
||||
}
|
||||
@ -5,69 +5,66 @@ from functools import reduce
|
||||
from freqtrade.persistence import Trade
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from typing import Dict, Optional
|
||||
from technical import qtpylib
|
||||
from typing import Dict
|
||||
from freqtrade.strategy import IStrategy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FreqaiPrimer(IStrategy):
|
||||
"""
|
||||
策略说明:
|
||||
- 所有交易信号基于 FreqAI 的动态预测
|
||||
- 使用多目标回归模型预测趋势强度、波动率、ROI、止损和背离评分
|
||||
- 入场/出场基于连续背离信号
|
||||
- 不使用 Hyperopt 优化参数
|
||||
- 不包含任何分类列(如 market_condition)
|
||||
- 只使用回归模型预测价值背离(&-price_value_divergence)
|
||||
- 动态调整阈值,基于 price_value_divergence 的历史数据
|
||||
- 放宽过滤条件,增加信号触发频率
|
||||
- 使用RSI和布林带作为辅助过滤条件
|
||||
"""
|
||||
|
||||
# 👇 币种特定参数(可选)
|
||||
PAIR_PARAMS = {
|
||||
"BTC/USDT": {"volatility_factor": 1.0, "min_stop": -0.005},
|
||||
"TON/USDT": {"volatility_factor": 1.3, "min_stop": -0.0035},
|
||||
"DOGE/USDT": {"volatility_factor": 1.5, "min_stop": -0.003},
|
||||
"DOT/USDT": {"volatility_factor": 1.3, "min_stop": -0.0035},
|
||||
"XRP/USDT": {"volatility_factor": 1.35, "min_stop": -0.0032},
|
||||
"OKB/USDT": {"volatility_factor": 0.9, "min_stop": -0.006},
|
||||
"SOL/USDT": {"volatility_factor": 1.4, "min_stop": -0.0032},
|
||||
"WCT/USDT": {"volatility_factor": 1.4, "min_stop": -0.004},
|
||||
"TRUMP/USDT": {"volatility_factor": 1.4, "min_stop": -0.0035},
|
||||
"SUI/USDT": {"volatility_factor": 1.6, "min_stop": -0.0025},
|
||||
"PEPE/USDT": {"volatility_factor": 1.5, "min_stop": -0.0036},
|
||||
"TRB/USDT": {"volatility_factor": 1.45, "min_stop": -0.004},
|
||||
"MASK/USDT": {"volatility_factor": 1.65, "min_stop": -0.0045},
|
||||
"UNI/USDT": {"volatility_factor": 1.45, "min_stop": -0.005},
|
||||
"KAITO/USDT": {"volatility_factor": 1.45, "min_stop": -0.004},
|
||||
# 策略参数
|
||||
minimal_roi = {
|
||||
"0": 0.02, # 固定止盈2%
|
||||
"30": 0.01,
|
||||
"60": 0
|
||||
}
|
||||
|
||||
use_custom_stoploss = True
|
||||
trailing_stop = False # 关闭 trailing_stop,避免干扰背离信号
|
||||
use_dynamic_roi = True
|
||||
position_adjuster = True
|
||||
stoploss = -0.015 # 固定止损-1.5%
|
||||
|
||||
timeframe = "3m" # 3分钟K线
|
||||
|
||||
use_custom_stoploss = False # 不使用动态止损,改为固定止损
|
||||
|
||||
# 绘图配置
|
||||
plot_config = {
|
||||
"main_plot": {},
|
||||
"main_plot": {
|
||||
"ema200": {"color": "blue"},
|
||||
"bb_upperband": {"color": "gray"},
|
||||
"bb_lowerband": {"color": "gray"},
|
||||
"bb_middleband": {"color": "gray"}
|
||||
},
|
||||
"subplots": {
|
||||
"Signals": {
|
||||
"enter_long": {"color": "green"},
|
||||
"exit_long": {"color": "red"}
|
||||
},
|
||||
"FreqAI Predictions": {
|
||||
"&-trend_strength": {"color": "blue"},
|
||||
"&-volatility_forecast": {"color": "orange"},
|
||||
"&-divergence_score": {"color": "purple"}
|
||||
"Price-Value Divergence": {
|
||||
"&-price_value_divergence": {"color": "purple"}
|
||||
},
|
||||
"Volume Z-Score": {
|
||||
"volume_z_score": {"color": "orange"}
|
||||
},
|
||||
"RSI": {
|
||||
"rsi": {"color": "cyan"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# FreqAI 配置:回归模型(预测价值背离)
|
||||
freqai_info = {
|
||||
"model": "LightGBMMultiTargetRegressor",
|
||||
"identifier": "divergence_model",
|
||||
"model": "LightGBMRegressor",
|
||||
"feature_parameters": {
|
||||
"include_timeframes": ["5m", "15m", "1h"],
|
||||
"label_period_candles": 24,
|
||||
"include_timeframes": ["3m", "15m", "1h"],
|
||||
"label_period_candles": 12,
|
||||
"include_shifted_candles": 3,
|
||||
"principal_component_analysis": True,
|
||||
},
|
||||
"data_split_parameters": {
|
||||
"test_size": 0.2,
|
||||
@ -79,18 +76,18 @@ class FreqaiPrimer(IStrategy):
|
||||
"num_leaves": 31,
|
||||
"verbose": -1,
|
||||
},
|
||||
"fit_live_predictions_candles": 100,
|
||||
}
|
||||
|
||||
def __init__(self, config: dict, *args, **kwargs):
|
||||
super().__init__(config, *args, **kwargs)
|
||||
|
||||
# 设置日志级别为 DEBUG
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.debug("✅ 策略已初始化,日志级别设置为 DEBUG")
|
||||
|
||||
# 👇 特征工程函数
|
||||
def feature_engineering_expand_all(self, dataframe: DataFrame, period: int, metadata: dict, **kwargs) -> DataFrame:
|
||||
# 保留原有指标
|
||||
"""
|
||||
特征工程:计算技术指标作为FreqAI的输入特征
|
||||
"""
|
||||
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
|
||||
dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period)
|
||||
dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)
|
||||
@ -100,192 +97,189 @@ class FreqaiPrimer(IStrategy):
|
||||
dataframe["bb_lowerband-period"] = lowerband
|
||||
dataframe["bb_upperband-period"] = upperband
|
||||
dataframe["bb_middleband-period"] = middleband
|
||||
dataframe["%-bb_width-period"] = (dataframe["bb_upperband-period"] - dataframe["bb_middleband-period"]) / dataframe["bb_middleband-period"]
|
||||
dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period)
|
||||
dataframe["%-bb_width-period"] = (dataframe["bb_upperband-period"] - dataframe["bb_lowerband-period"]) / dataframe["bb_middleband-period"]
|
||||
|
||||
# 添加策略 B 的指标
|
||||
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period) # 资金流指标
|
||||
dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period) # 趋势强度
|
||||
dataframe["%-tema-period"] = ta.TEMA(dataframe, timeperiod=period) # TEMA 指标
|
||||
dataframe["%-relative_volume-period"] = dataframe["volume"] / dataframe["volume"].rolling(period).mean() # 相对成交量
|
||||
dataframe["%-close-bb_lower-period"] = dataframe["close"] / dataframe["bb_lowerband-period"] # 收盘价/布林带下轨
|
||||
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
|
||||
dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period)
|
||||
dataframe["%-relative_volume-period"] = dataframe["volume"] / dataframe["volume"].rolling(period).mean()
|
||||
|
||||
# 新增:与背离相关的衍生特征
|
||||
dataframe["%-price_trend_diff-period"] = dataframe["close"] - dataframe["%-ema-period"] # 价格与趋势偏离
|
||||
dataframe["%-roc_mfi_ratio-period"] = dataframe["%-roc-period"] / (dataframe["%-mfi-period"].replace(0, 1)) # ROC/MFI 比率
|
||||
# 计算价值背离作为特征
|
||||
dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)
|
||||
dataframe["%-price_value_divergence"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"]
|
||||
|
||||
# 数据清理
|
||||
columns_to_clean = [
|
||||
"%-rsi-period", "%-mfi-period", "%-sma-period", "%-ema-period", "%-adx-period",
|
||||
"bb_lowerband-period", "bb_middleband-period", "bb_upperband-period",
|
||||
"%-bb_width-period", "%-roc-period", "%-relative_volume-period", "%-close-bb_lower-period", "%-tema-period",
|
||||
"%-price_trend_diff-period", "%-roc_mfi_ratio-period"
|
||||
"%-bb_width-period", "%-relative_volume-period", "%-price_value_divergence"
|
||||
]
|
||||
for col in columns_to_clean:
|
||||
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], np.nan)
|
||||
dataframe[col] = dataframe[col].ffill().fillna(0)
|
||||
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], 0).ffill().fillna(0)
|
||||
|
||||
logger.debug(f"[{metadata['pair']}] 特征工程完成,列:{list(dataframe.columns)}")
|
||||
return dataframe
|
||||
|
||||
# 👇 定义目标列(全部为连续值)
|
||||
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
|
||||
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
|
||||
"""
|
||||
设置FreqAI的训练目标:只预测价值背离
|
||||
"""
|
||||
if len(dataframe) < 200:
|
||||
logger.warning(f"[{metadata['pair']}] 数据量不足({len(dataframe)}根K线),需要至少200根K线进行训练")
|
||||
return dataframe
|
||||
|
||||
# 1. 趋势强度
|
||||
dataframe["&-trend_strength"] = dataframe["close"].pct_change(label_period)
|
||||
# 计算200周期EMA作为公平价值
|
||||
dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)
|
||||
# 计算价值背离(回归目标)
|
||||
dataframe["&-price_value_divergence"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"]
|
||||
|
||||
# 2. 波动率预测
|
||||
dataframe["&-volatility_forecast"] = dataframe["close"].pct_change(label_period).abs()
|
||||
# 成交量Z分数
|
||||
dataframe["volume_mean_20"] = dataframe["volume"].rolling(20).mean()
|
||||
dataframe["volume_std_20"] = dataframe["volume"].rolling(20).std()
|
||||
dataframe["volume_z_score"] = (dataframe["volume"] - dataframe["volume_mean_20"]) / dataframe["volume_std_20"]
|
||||
|
||||
# 3. ROI 目标
|
||||
dataframe["&-roi_target"] = np.where(
|
||||
dataframe["&-trend_strength"] > 0,
|
||||
dataframe["&-trend_strength"] * 1.5,
|
||||
0.01
|
||||
)
|
||||
|
||||
# 4. 动态止损目标
|
||||
dataframe["&-stoploss_target"] = -dataframe["&-volatility_forecast"] * 1.2
|
||||
|
||||
# 5. 连续背离评分
|
||||
dataframe["price_change"] = dataframe["close"].pct_change(label_period)
|
||||
dataframe["trend_change"] = dataframe["&-trend_strength"].pct_change(label_period)
|
||||
dataframe["&-divergence_score"] = dataframe["price_change"] - dataframe["trend_change"]
|
||||
|
||||
# 👇 新增:清理所有目标列中的非有限值 + clip 背离评分
|
||||
target_columns = [
|
||||
"&-trend_strength",
|
||||
"&-volatility_forecast",
|
||||
"&-roi_target",
|
||||
"&-stoploss_target",
|
||||
"&-divergence_score"
|
||||
]
|
||||
|
||||
for col in target_columns:
|
||||
if col in dataframe.columns:
|
||||
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], np.nan)
|
||||
dataframe[col] = dataframe[col].ffill().fillna(0)
|
||||
|
||||
# 对背离评分做裁剪,防止极端值干扰交易信号
|
||||
dataframe["&-divergence_score"] = dataframe["&-divergence_score"].clip(-1, 1)
|
||||
# 数据清理
|
||||
dataframe["&-price_value_divergence"] = dataframe["&-price_value_divergence"].replace([np.inf, -np.inf], 0).ffill().fillna(0)
|
||||
dataframe["volume_z_score"] = dataframe["volume_z_score"].replace([np.inf, -np.inf], 0).ffill().fillna(0)
|
||||
|
||||
logger.debug(f"[{metadata['pair']}] 目标列生成完成,列:{list(dataframe.columns)}")
|
||||
return dataframe
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
dataframe = self.freqai.start(dataframe, metadata, self)
|
||||
"""
|
||||
计算所有指标,包括FreqAI预测结果,并基于历史数据动态调整阈值
|
||||
"""
|
||||
logger.info(f"[{metadata['pair']}] 当前可用列(调用FreqAI前):{list(dataframe.columns)}")
|
||||
|
||||
if self.freqai and hasattr(self.freqai, "data"):
|
||||
labels_mean = self.freqai.data.labels_mean
|
||||
labels_std = self.freqai.data.labels_std
|
||||
# 计算200周期EMA和历史价值背离
|
||||
dataframe["ema200"] = ta.EMA(dataframe, timeperiod=200)
|
||||
dataframe["price_value_divergence"] = (dataframe["close"] - dataframe["ema200"]) / dataframe["ema200"]
|
||||
|
||||
dataframe["mean_trend"] = labels_mean.get("&-trend_strength", 0.0001)
|
||||
dataframe["std_trend"] = labels_std.get("&-trend_strength", 0.0031)
|
||||
# 动态计算阈值:基于 price_value_divergence 的历史均值和标准差
|
||||
if "price_value_divergence" in dataframe.columns:
|
||||
divergence_mean = dataframe["price_value_divergence"].mean()
|
||||
divergence_std = dataframe["price_value_divergence"].std()
|
||||
k = 1.5 # 标准差倍数,可以调整
|
||||
self.buy_threshold = max(divergence_mean - k * divergence_std, -0.05) # 设置下限,避免过低
|
||||
self.sell_threshold = min(divergence_mean + k * divergence_std, 0.05) # 设置上限,避免过高
|
||||
# 确保阈值至少有一定宽度
|
||||
self.buy_threshold = min(self.buy_threshold, -0.005)
|
||||
self.sell_threshold = max(self.sell_threshold, 0.005)
|
||||
logger.info(f"[{metadata['pair']}] price_value_divergence 历史均值:{divergence_mean:.4f}")
|
||||
logger.info(f"[{metadata['pair']}] price_value_divergence 历史标准差:{divergence_std:.4f}")
|
||||
logger.info(f"[{metadata['pair']}] 动态买入阈值:{self.buy_threshold:.4f}")
|
||||
logger.info(f"[{metadata['pair']}] 动态卖出阈值:{self.sell_threshold:.4f}")
|
||||
else:
|
||||
self.buy_threshold = -0.015 # 回退阈值
|
||||
self.sell_threshold = 0.015
|
||||
logger.warning(f"[{metadata['pair']}] 无法计算动态阈值,使用默认阈值 ±0.015")
|
||||
|
||||
dataframe["mean_volatility"] = labels_mean.get("&-volatility_forecast", 0.0021)
|
||||
dataframe["std_volatility"] = labels_std.get("&-volatility_forecast", 0.0022)
|
||||
# 调用FreqAI预测价值背离
|
||||
if not hasattr(self, 'freqai') or self.freqai is None:
|
||||
logger.error(f"[{metadata['pair']}] FreqAI 未初始化,请确保回测命令中启用了 --freqai")
|
||||
# 回退到规则计算
|
||||
dataframe["&-price_value_divergence"] = dataframe["price_value_divergence"]
|
||||
else:
|
||||
dataframe = self.freqai.start(dataframe, metadata, self)
|
||||
|
||||
dataframe["mean_divergence"] = labels_mean.get("&-divergence_score", 0.0)
|
||||
dataframe["std_divergence"] = labels_std.get("&-divergence_score", 1.0)
|
||||
# 检查预测结果
|
||||
if "&-price_value_divergence" not in dataframe.columns:
|
||||
logger.warning(f"[{metadata['pair']}] 回归模型未生成 &-price_value_divergence,回退到规则计算")
|
||||
dataframe["&-price_value_divergence"] = dataframe["price_value_divergence"]
|
||||
|
||||
# 计算其他指标
|
||||
upperband, middleband, lowerband = ta.BBANDS(dataframe["close"], timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
|
||||
dataframe["bb_upperband"] = upperband
|
||||
dataframe["bb_middleband"] = middleband
|
||||
dataframe["bb_lowerband"] = lowerband
|
||||
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
|
||||
dataframe["volume_mean_20"] = dataframe["volume"].rolling(20).mean()
|
||||
dataframe["volume_std_20"] = dataframe["volume"].rolling(20).std()
|
||||
dataframe["volume_z_score"] = (dataframe["volume"] - dataframe["volume_mean_20"]) / dataframe["volume_std_20"]
|
||||
|
||||
# 数据清理
|
||||
for col in ["ema200", "bb_upperband", "bb_middleband", "bb_lowerband", "rsi", "volume_z_score", "&-price_value_divergence", "price_value_divergence"]:
|
||||
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], 0).ffill().fillna(0)
|
||||
|
||||
logger.info(f"[{metadata['pair']}] 指标计算完成,列:{list(dataframe.columns)}")
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
入场逻辑:基于动态阈值和放宽的过滤条件
|
||||
"""
|
||||
conditions = []
|
||||
|
||||
if "&-divergence_score" in df.columns:
|
||||
|
||||
mean_divergence = df["mean_divergence"].iloc[-1] if "mean_divergence" in df.columns else 0
|
||||
std_divergence = df["std_divergence"].iloc[-1] if "std_divergence" in df.columns else 1
|
||||
|
||||
# 计算 Z-score
|
||||
z_score_divergence = (df["&-divergence_score"] - mean_divergence) / std_divergence
|
||||
|
||||
# 正向背离:Z-score > 1
|
||||
buy_condition = z_score_divergence > 1
|
||||
|
||||
# 结合趋势强度过滤
|
||||
if "&-trend_strength" in df.columns:
|
||||
buy_condition &= df["&-trend_strength"] > 0.005
|
||||
|
||||
# 成交量过滤:成交量需大于 20 日均值的 1 倍
|
||||
df["vol_mean"] = df["volume"].rolling(20).mean()
|
||||
buy_condition &= (df["volume"] > df["vol_mean"] * 1)
|
||||
|
||||
if "&-price_value_divergence" in df.columns:
|
||||
# 买入条件:价格低估且放量
|
||||
buy_condition = (df["&-price_value_divergence"] < self.buy_threshold)
|
||||
buy_condition &= (df["volume_z_score"] > 1.5) # 降低成交量要求
|
||||
# RSI超卖过滤(放宽条件)
|
||||
buy_condition &= (df["rsi"] < 40)
|
||||
# 价格触及布林带下轨
|
||||
buy_condition &= (df["close"] <= df["bb_lowerband"])
|
||||
conditions.append(buy_condition)
|
||||
else:
|
||||
logger.warning("⚠️ &-price_value_divergence 列缺失,跳过该条件")
|
||||
|
||||
if len(conditions):
|
||||
if len(conditions) > 0:
|
||||
df.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1
|
||||
logger.info(f"[{metadata['pair']}] 入场信号触发,条件满足")
|
||||
|
||||
return df
|
||||
|
||||
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
出场逻辑:基于动态阈值和放宽的过滤条件
|
||||
"""
|
||||
conditions = []
|
||||
|
||||
if "&-divergence_score" in df.columns:
|
||||
|
||||
mean_divergence = df["mean_divergence"].iloc[-1] if "mean_divergence" in df.columns else 0
|
||||
std_divergence = df["std_divergence"].iloc[-1] if "std_divergence" in df.columns else 1
|
||||
|
||||
# 计算 Z-score
|
||||
z_score_divergence = (df["&-divergence_score"] - mean_divergence) / std_divergence
|
||||
|
||||
# 负向背离:Z-score < -1
|
||||
sell_condition = z_score_divergence < -1
|
||||
|
||||
# 结合趋势强度过滤
|
||||
if "&-trend_strength" in df.columns:
|
||||
sell_condition &= df["&-trend_strength"] < -0.005
|
||||
|
||||
if "&-price_value_divergence" in df.columns:
|
||||
# 卖出条件:价格高估
|
||||
sell_condition = (df["&-price_value_divergence"] > self.sell_threshold)
|
||||
sell_condition &= (df["volume_z_score"] > 1.5) # 降低成交量要求
|
||||
# RSI超买过滤(放宽条件)
|
||||
sell_condition |= (df["rsi"] > 60)
|
||||
conditions.append(sell_condition)
|
||||
else:
|
||||
logger.warning("⚠️ &-price_value_divergence 列缺失,跳过该条件")
|
||||
|
||||
if len(conditions):
|
||||
if len(conditions) > 0:
|
||||
df.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1
|
||||
logger.info(f"[{metadata['pair']}] 出场信号触发,条件满足")
|
||||
|
||||
return df
|
||||
|
||||
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
|
||||
current_rate: float, current_profit: float, **kwargs) -> float:
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||
last_candle = dataframe.iloc[-1]
|
||||
|
||||
volatility = last_candle.get("&-volatility_forecast", 0.0021)
|
||||
dynamic_stoploss = -volatility * 2 # 设置为波动率的两倍
|
||||
dynamic_stoploss = max(dynamic_stoploss, -0.01) # 最大不超过 1%
|
||||
|
||||
return dynamic_stoploss
|
||||
|
||||
def adjust_trade_position(self, trade: Trade, current_time: datetime,
|
||||
current_rate: float, current_profit: float,
|
||||
min_roi: Dict[float, float], max_profit: float) -> Optional[Dict[float, float]]:
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
|
||||
last_candle = dataframe.iloc[-1]
|
||||
|
||||
roi_target = last_candle.get("&-roi_target", 0.0065)
|
||||
mean_roi = last_candle.get("mean_roi", 0.0065)
|
||||
std_roi = last_candle.get("std_roi", 0.0041)
|
||||
|
||||
z_score_roi = (roi_target - mean_roi) / std_roi if std_roi != 0 else 0
|
||||
|
||||
if z_score_roi > 1:
|
||||
return {
|
||||
"0": roi_target * 1.2,
|
||||
"30": roi_target * 0.9,
|
||||
"60": roi_target * 0.6,
|
||||
"120": roi_target * 0.3,
|
||||
"180": 0.01
|
||||
}
|
||||
elif z_score_roi > 0:
|
||||
return {
|
||||
"0": roi_target * 1.0,
|
||||
"20": roi_target * 0.7,
|
||||
"60": roi_target * 0.3,
|
||||
"120": 0.01
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"0": max(0.005, roi_target),
|
||||
"60": 0.005,
|
||||
"180": 0.005
|
||||
}
|
||||
min_roi: Dict[float, float], max_profit: float):
|
||||
"""
|
||||
动态调整仓位:持仓时间超过30分钟后强制平仓
|
||||
"""
|
||||
hold_time = (current_time - trade.open_date_utc).total_seconds() / 60
|
||||
if hold_time > 30:
|
||||
logger.info(f"[{trade.pair}] 持仓时间超过30分钟,强制平仓")
|
||||
return -1
|
||||
return None
|
||||
|
||||
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
|
||||
time_in_force: str, current_time: datetime, **kwargs) -> bool:
|
||||
"""
|
||||
交易确认:确保没有快速重复交易
|
||||
"""
|
||||
recent_trades = Trade.get_trades(
|
||||
pair=pair,
|
||||
is_open=False,
|
||||
close_date=current_time - datetime.timedelta(minutes=5)
|
||||
).all()
|
||||
if len(recent_trades) > 0:
|
||||
logger.info(f"[{pair}] 5分钟内有近期交易,跳过本次入场")
|
||||
return False
|
||||
return True
|
||||
|
||||
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
||||
rate: float, time_in_force: str, exit_reason: str,
|
||||
current_time: datetime, **kwargs) -> bool:
|
||||
"""
|
||||
交易退出确认:记录退出原因
|
||||
"""
|
||||
logger.info(f"[{pair}] 退出交易,原因:{exit_reason}, 利润:{trade.calc_profit_ratio(rate):.2%}")
|
||||
return True
|
||||
|
||||
11
tools/plot.sh
Executable file
11
tools/plot.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker-compose run --rm freqtrade plot-dataframe \
|
||||
--logfile /freqtrade/user_data/logs/freqtrade.log \
|
||||
--freqaimodel LightGBMRegressorMultiTarget \
|
||||
--config /freqtrade/config_examples/freqaiprimer.json \
|
||||
--strategy-path /freqtrade/templates \
|
||||
--strategy FreqaiPrimer \
|
||||
--timerange 20250410-20250411
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user