get_market_trend加入了长周期
This commit is contained in:
parent
eb1c40cbf5
commit
af12fe6b25
@ -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"
|
||||
}
|
||||
@ -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 和量价关系。
|
||||
|
||||
综合 3m、15m 和 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 到 100,log(x + 1) 确保非线性
|
||||
mapped_score = 50 + 50 * (np.log1p(raw_score / 50) / np.log1p(1))
|
||||
else:
|
||||
# 负向:0 到 50,log(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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user