get_market_trend加入了长周期

This commit is contained in:
zhangkun9038@dingtalk.com 2025-06-23 12:03:05 +08:00
parent eb1c40cbf5
commit af12fe6b25
2 changed files with 135 additions and 127 deletions

View File

@ -8,33 +8,33 @@
"max_open_trades": 5
},
"buy": {
"ADD_POSITION_THRESHOLD": -0.01,
"BUY_THRESHOLD_MAX": -0.011,
"BUY_THRESHOLD_MIN": -0.079,
"COOLDOWN_PERIOD_MINUTES": 8,
"MAX_ENTRY_POSITION_ADJUSTMENT": 1
"ADD_POSITION_THRESHOLD": -0.028,
"BUY_THRESHOLD_MAX": -0.003,
"BUY_THRESHOLD_MIN": -0.028,
"COOLDOWN_PERIOD_MINUTES": 4,
"MAX_ENTRY_POSITION_ADJUSTMENT": 2
},
"sell": {
"EXIT_POSITION_RATIO": 0.341,
"SELL_THRESHOLD_MAX": 0.082,
"SELL_THRESHOLD_MIN": 0.012,
"TRAILING_STOP_DISTANCE": 0.016,
"TRAILING_STOP_START": 0.026
"EXIT_POSITION_RATIO": 0.335,
"SELL_THRESHOLD_MAX": 0.054,
"SELL_THRESHOLD_MIN": 0.001,
"TRAILING_STOP_DISTANCE": 0.005,
"TRAILING_STOP_START": 0.048
},
"protection": {},
"roi": {
"0": 0.057,
"15": 0.025,
"22": 0.013,
"39": 0
"0": 0.156,
"20": 0.057999999999999996,
"50": 0.018,
"113": 0
},
"trailing": {
"trailing_stop": true,
"trailing_stop_positive": 0.139,
"trailing_stop_positive_offset": 0.23700000000000002,
"trailing_stop_positive": 0.172,
"trailing_stop_positive_offset": 0.23199999999999998,
"trailing_only_offset_is_reached": false
}
},
"ft_stratparam_v": 1,
"export_time": "2025-06-22 18:22:25.969981+00:00"
"export_time": "2025-06-23 00:37:56.498155+00:00"
}

View File

@ -537,131 +537,139 @@ class FreqaiPrimer(IStrategy):
return adjusted_rate
def get_market_trend(self, dataframe: DataFrame = None, metadata: dict = None) -> int:
"""
计算市场趋势得分0 表示强熊100 表示强牛50 表示中性
使用对数函数映射非线性增强两端趋势
合价格趋势K 线形态StochRSI 和量价关系
综合 3m15m 1h 时间框架的趋势使用对数函数映射非线性增强两端趋势
合价格趋势K 线形态StochRSI 和量价关系
参数
dataframe: 可选外部传入的 BTC/USDT 数据默认为 None自动获取
dataframe: 可选外部传入的 BTC/USDT 数据3m 时间框架默认为 None自动获取
metadata: 可选包含币种信息
返回值
int: 0-100 的趋势得分
"""
try:
# 获取 BTC/USDT 数据
if dataframe is None:
btc_df = self.dp.get_pair_dataframe("BTC/USDT", self.timeframe)
else:
btc_df = dataframe
# 初始化得分和权重
timeframes = ["3m", "15m", "1h"]
weights = {"3m": 0.5, "15m": 0.3, "1h": 0.2} # 短期框架权重更高
trend_scores = {}
if len(btc_df) < 200:
logger.warning("BTC 数据不足返回默认趋势得分50")
return 50
# 示例:使用 metadata 记录币种信息
pair = metadata.get('pair', 'Unknown') if metadata else 'Unknown'
logger.debug(f"[{pair}] 正在计算市场趋势得分")
# 获取 BTC/USDT 数据
btc_df = dataframe if dataframe is not None else self.dp.get_pair_dataframe("BTC/USDT", self.timeframe)
if len(btc_df) < 200:
logger.warning("BTC 数据不足返回默认趋势得分50")
return 50
logger.debug(f"[{pair}] 正在计算多时间框架市场趋势得分")
# --- 价格趋势 ---
btc_df["ema50"] = ta.EMA(btc_df, timeperiod=50)
btc_df["ema200"] = ta.EMA(btc_df, timeperiod=200)
btc_df["ema50_slope"] = (btc_df["ema50"] - btc_df["ema50"].shift(10)) / btc_df["ema50"].shift(10)
price_above_ema = btc_df["close"].iloc[-1] > btc_df["ema200"].iloc[-1]
ema50_above_ema200 = btc_df["ema50"].iloc[-1] > btc_df["ema200"].iloc[-1]
ema50_slope = btc_df["ema50_slope"].iloc[-1]
price_score = 0
if price_above_ema:
price_score += 20
if ema50_above_ema200:
price_score += 20
if ema50_slope > 0.005:
price_score += 15
elif ema50_slope < -0.005:
price_score -= 15
for tf in timeframes:
# 获取对应时间框架的数据
if tf == "3m" and dataframe is not None:
btc_df = dataframe
else:
btc_df = self.dp.get_pair_dataframe("BTC/USDT", tf)
# --- K 线形态 ---
btc_df["bullish_engulfing"] = (
(btc_df["close"].shift(1) < btc_df["open"].shift(1)) & # 前一天阴线
(btc_df["close"] > btc_df["open"]) & # 当天阳线
(btc_df["close"] > btc_df["open"].shift(1)) & # 吞没前一天开盘价
(btc_df["open"] < btc_df["close"].shift(1)) # 吞没前一天收盘价
)
btc_df["bearish_engulfing"] = (
(btc_df["close"].shift(1) > btc_df["open"].shift(1)) & # 前一天阳线
(btc_df["close"] < btc_df["open"]) & # 当天阴线
(btc_df["close"] < btc_df["open"].shift(1)) & # 吞没前一天开盘价
(btc_df["open"] > btc_df["close"].shift(1)) # 吞没前一天收盘价
)
kline_score = 0
if btc_df["bullish_engulfing"].iloc[-1]:
kline_score += 15
elif btc_df["bearish_engulfing"].iloc[-1]:
kline_score -= 15
# 近期波动性
volatility = btc_df["close"].pct_change(10).std() * 100
if volatility > 0.5:
kline_score += 10 if price_score > 0 else -10
# 检查数据量
min_candles = 200 if tf == "3m" else 100 if tf == "15m" else 50 # 不同时间框架所需最小数据量
if len(btc_df) < min_candles:
logger.warning(f"BTC 数据不足({tf}{len(btc_df)} 根K线使用默认得分50")
trend_scores[tf] = 50
continue
# --- StochRSI ---
stochrsi = ta.STOCHRSI(btc_df, timeperiod=14, fastk_period=3, fastd_period=3)
btc_df["stochrsi_k"] = stochrsi["fastk"]
btc_df["stochrsi_d"] = stochrsi["fastd"]
stochrsi_score = 0
stochrsi_k = btc_df["stochrsi_k"].iloc[-1]
stochrsi_d = btc_df["stochrsi_d"].iloc[-1]
if stochrsi_k > 80 and stochrsi_k < stochrsi_d:
stochrsi_score -= 15 # 超买且 K 下穿 D
elif stochrsi_k < 20 and stochrsi_k > stochrsi_d:
stochrsi_score += 15 # 超卖且 K 上穿 D
elif stochrsi_k > 50:
stochrsi_score += 5
elif stochrsi_k < 50:
stochrsi_score -= 5
# --- 价格趋势 ---
ema_short_period = 50 if tf == "3m" else 20 if tf == "15m" else 12 # 调整周期以适应时间框架
ema_long_period = 200 if tf == "3m" else 80 if tf == "15m" else 50
btc_df["ema_short"] = ta.EMA(btc_df, timeperiod=ema_short_period)
btc_df["ema_long"] = ta.EMA(btc_df, timeperiod=ema_long_period)
btc_df["ema_short_slope"] = (btc_df["ema_short"] - btc_df["ema_short"].shift(10)) / btc_df["ema_short"].shift(10)
# --- 量价关系 ---
btc_df["volume_mean_20"] = btc_df["volume"].rolling(20).mean()
btc_df["volume_std_20"] = btc_df["volume"].rolling(20).std()
btc_df["volume_z_score"] = (btc_df["volume"] - btc_df["volume_mean_20"]) / btc_df["volume_std_20"]
btc_df["adx"] = ta.ADX(btc_df, timeperiod=14)
volume_score = 0
if btc_df["volume_z_score"].iloc[-1] > 1.5:
volume_score += 10 if price_score > 0 else -10
if btc_df["adx"].iloc[-1] > 25:
volume_score += 10 if price_score > 0 else -10
price_above_ema = btc_df["close"].iloc[-1] > btc_df["ema_long"].iloc[-1]
ema_short_above_ema_long = btc_df["ema_short"].iloc[-1] > btc_df["ema_long"].iloc[-1]
ema_short_slope = btc_df["ema_short_slope"].iloc[-1]
# --- 综合得分 ---
raw_score = price_score + kline_score + stochrsi_score + volume_score
# 归一化到 [-50, 50]
raw_score = max(min(raw_score, 50), -50)
# 对数映射到 [0, 100]50 为中性
if raw_score >= 0:
# 正向50 到 100log(x + 1) 确保非线性
mapped_score = 50 + 50 * (np.log1p(raw_score / 50) / np.log1p(1))
else:
# 负向0 到 50log(1 - x)
mapped_score = 50 * (np.log1p(-raw_score / 50) / np.log1p(1))
final_score = int(round(mapped_score))
price_score = 0
if price_above_ema:
price_score += 20
if ema_short_above_ema_long:
price_score += 20
if ema_short_slope > 0.005:
price_score += 15
elif ema_short_slope < -0.005:
price_score -= 15
# --- K 线形态 ---
btc_df["bullish_engulfing"] = (
(btc_df["close"].shift(1) < btc_df["open"].shift(1)) &
(btc_df["close"] > btc_df["open"]) &
(btc_df["close"] > btc_df["open"].shift(1)) &
(btc_df["open"] < btc_df["close"].shift(1))
)
btc_df["bearish_engulfing"] = (
(btc_df["close"].shift(1) > btc_df["open"].shift(1)) &
(btc_df["close"] < btc_df["open"]) &
(btc_df["close"] < btc_df["open"].shift(1)) &
(btc_df["open"] > btc_df["close"].shift(1))
)
kline_score = 0
if btc_df["bullish_engulfing"].iloc[-1]:
kline_score += 15
elif btc_df["bearish_engulfing"].iloc[-1]:
kline_score -= 15
volatility = btc_df["close"].pct_change(10).std() * 100
if volatility > 0.5:
kline_score += 10 if price_score > 0 else -10
# --- StochRSI ---
stochrsi = ta.STOCHRSI(btc_df, timeperiod=14, fastk_period=3, fastd_period=3)
btc_df["stochrsi_k"] = stochrsi["fastk"]
btc_df["stochrsi_d"] = stochrsi["fastd"]
stochrsi_score = 0
stochrsi_k = btc_df["stochrsi_k"].iloc[-1]
stochrsi_d = btc_df["stochrsi_d"].iloc[-1]
if stochrsi_k > 80 and stochrsi_k < stochrsi_d:
stochrsi_score -= 15
elif stochrsi_k < 20 and stochrsi_k > stochrsi_d:
stochrsi_score += 15
elif stochrsi_k > 50:
stochrsi_score += 5
elif stochrsi_k < 50:
stochrsi_score -= 5
# --- 量价关系 ---
btc_df["volume_mean_20"] = btc_df["volume"].rolling(20).mean()
btc_df["volume_std_20"] = btc_df["volume"].rolling(20).std()
btc_df["volume_z_score"] = (btc_df["volume"] - btc_df["volume_mean_20"]) / btc_df["volume_std_20"]
btc_df["adx"] = ta.ADX(btc_df, timeperiod=14)
volume_score = 0
if btc_df["volume_z_score"].iloc[-1] > 1.5:
volume_score += 10 if price_score > 0 else -10
if btc_df["adx"].iloc[-1] > 25:
volume_score += 10 if price_score > 0 else -10
# --- 综合得分 ---
raw_score = price_score + kline_score + stochrsi_score + volume_score
raw_score = max(min(raw_score, 50), -50)
# 对数映射到 [0, 100]
if raw_score >= 0:
mapped_score = 50 + 50 * (np.log1p(raw_score / 50) / np.log1p(1))
else:
mapped_score = 50 * (np.log1p(-raw_score / 50) / np.log1p(1))
trend_scores[tf] = max(0, min(100, int(round(mapped_score))))
logger.debug(f"[{pair}] {tf} 趋势得分:{trend_scores[tf]}, 原始得分:{raw_score}, "
f"价格得分:{price_score}, K线得分{kline_score}, "
f"StochRSI得分{stochrsi_score}, 量价得分:{volume_score}")
# 加权融合多时间框架得分
final_score = sum(trend_scores[tf] * weights[tf] for tf in timeframes)
final_score = int(round(final_score))
final_score = max(0, min(100, final_score))
logger.debug(f"BTC 趋势得分:{final_score}, 原始得分:{raw_score}, "
f"价格得分:{price_score}, K线得分{kline_score}, "
f"StochRSI得分{stochrsi_score}, 量价得分:{volume_score}")
logger.info(f"[{pair}] 最终趋势得分:{final_score}, "
f"3m得分{trend_scores.get('3m', 50)}, 15m得分{trend_scores.get('15m', 50)}, "
f"1h得分{trend_scores.get('1h', 50)}")
return final_score
except Exception as e:
logger.error(f"获取市场趋势失败:{e}", exc_info=True)
logger.error(f"[{pair}] 获取市场趋势失败:{e}", exc_info=True)
return 50