feat(config): 添加4H趋势过滤配置以优化交易策略
在配置管理中新增 `BLOCK_SHORT_WHEN_4H_UP` 参数,允许在4H上涨时禁止开空,增强策略灵活性与风险控制。同时,更新前端组件以展示该配置,提升用户体验。此改动确保在不同市场条件下,策略能够更有效地避免逆势操作。
This commit is contained in:
parent
cbf778d560
commit
cddcf35481
|
|
@ -951,6 +951,7 @@ class ConfigManager:
|
||||||
# 市场状态方案(便于在不同行情间切换)
|
# 市场状态方案(便于在不同行情间切换)
|
||||||
'MARKET_SCHEME': str(eff_get('MARKET_SCHEME', 'normal') or 'normal').lower(),
|
'MARKET_SCHEME': str(eff_get('MARKET_SCHEME', 'normal') or 'normal').lower(),
|
||||||
'BLOCK_LONG_WHEN_4H_DOWN': eff_get('BLOCK_LONG_WHEN_4H_DOWN', False), # 4H 下跌时禁止开多(熊市/保守用)
|
'BLOCK_LONG_WHEN_4H_DOWN': eff_get('BLOCK_LONG_WHEN_4H_DOWN', False), # 4H 下跌时禁止开多(熊市/保守用)
|
||||||
|
'BLOCK_SHORT_WHEN_4H_UP': eff_get('BLOCK_SHORT_WHEN_4H_UP', True), # 4H 上涨时禁止开空(默认 True,避免逆势做空)
|
||||||
}
|
}
|
||||||
|
|
||||||
# 根据市场方案覆盖关键参数(便于快速切换熊市/牛市/保守等预设)
|
# 根据市场方案覆盖关键参数(便于快速切换熊市/牛市/保守等预设)
|
||||||
|
|
@ -960,12 +961,14 @@ class ConfigManager:
|
||||||
'MAX_POSITION_PERCENT': 0.12,
|
'MAX_POSITION_PERCENT': 0.12,
|
||||||
'ATR_STOP_LOSS_MULTIPLIER': 2.5,
|
'ATR_STOP_LOSS_MULTIPLIER': 2.5,
|
||||||
'BLOCK_LONG_WHEN_4H_DOWN': False,
|
'BLOCK_LONG_WHEN_4H_DOWN': False,
|
||||||
|
'BLOCK_SHORT_WHEN_4H_UP': True, # 4H 上涨不开空
|
||||||
},
|
},
|
||||||
'bear': {
|
'bear': {
|
||||||
'MIN_STOP_LOSS_PRICE_PCT': 0.05, # 放宽止损约 -5%
|
'MIN_STOP_LOSS_PRICE_PCT': 0.05, # 放宽止损约 -5%
|
||||||
'MAX_POSITION_PERCENT': 0.08, # 单仓 ≤ 8%
|
'MAX_POSITION_PERCENT': 0.08, # 单仓 ≤ 8%
|
||||||
'ATR_STOP_LOSS_MULTIPLIER': 2.5,
|
'ATR_STOP_LOSS_MULTIPLIER': 2.5,
|
||||||
'BLOCK_LONG_WHEN_4H_DOWN': True, # 4H 下跌不开多
|
'BLOCK_LONG_WHEN_4H_DOWN': True, # 4H 下跌不开多
|
||||||
|
'BLOCK_SHORT_WHEN_4H_UP': True, # 4H 上涨不开空
|
||||||
'BETA_FILTER_ENABLED': True,
|
'BETA_FILTER_ENABLED': True,
|
||||||
},
|
},
|
||||||
'bull': {
|
'bull': {
|
||||||
|
|
@ -973,12 +976,14 @@ class ConfigManager:
|
||||||
'MAX_POSITION_PERCENT': 0.12,
|
'MAX_POSITION_PERCENT': 0.12,
|
||||||
'ATR_STOP_LOSS_MULTIPLIER': 2.0,
|
'ATR_STOP_LOSS_MULTIPLIER': 2.0,
|
||||||
'BLOCK_LONG_WHEN_4H_DOWN': False,
|
'BLOCK_LONG_WHEN_4H_DOWN': False,
|
||||||
|
'BLOCK_SHORT_WHEN_4H_UP': True, # 4H 上涨不开空(牛市尤需)
|
||||||
},
|
},
|
||||||
'conservative': {
|
'conservative': {
|
||||||
'MIN_STOP_LOSS_PRICE_PCT': 0.06, # 最宽松止损
|
'MIN_STOP_LOSS_PRICE_PCT': 0.06, # 最宽松止损
|
||||||
'MAX_POSITION_PERCENT': 0.06, # 最小仓位
|
'MAX_POSITION_PERCENT': 0.06, # 最小仓位
|
||||||
'ATR_STOP_LOSS_MULTIPLIER': 2.5,
|
'ATR_STOP_LOSS_MULTIPLIER': 2.5,
|
||||||
'BLOCK_LONG_WHEN_4H_DOWN': True,
|
'BLOCK_LONG_WHEN_4H_DOWN': True,
|
||||||
|
'BLOCK_SHORT_WHEN_4H_UP': True,
|
||||||
'BETA_FILTER_ENABLED': True,
|
'BETA_FILTER_ENABLED': True,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,8 @@ DEFAULTS_TO_SYNC = [
|
||||||
"description": "市场方案:normal / bear / bull / conservative。切换后自动覆盖止损、仓位、趋势过滤等参数。"},
|
"description": "市场方案:normal / bear / bull / conservative。切换后自动覆盖止损、仓位、趋势过滤等参数。"},
|
||||||
{"config_key": "BLOCK_LONG_WHEN_4H_DOWN", "config_value": "false", "config_type": "boolean", "category": "strategy",
|
{"config_key": "BLOCK_LONG_WHEN_4H_DOWN", "config_value": "false", "config_type": "boolean", "category": "strategy",
|
||||||
"description": "4H 趋势下跌时禁止开多。bear / conservative 方案下自动为 true。"},
|
"description": "4H 趋势下跌时禁止开多。bear / conservative 方案下自动为 true。"},
|
||||||
|
{"config_key": "BLOCK_SHORT_WHEN_4H_UP", "config_value": "true", "config_type": "boolean", "category": "strategy",
|
||||||
|
"description": "4H 趋势上涨时禁止开空。默认 true,避免逆势做空导致止损。"},
|
||||||
{"config_key": "AUTO_MARKET_SCHEME_ENABLED", "config_value": "false", "config_type": "boolean", "category": "strategy",
|
{"config_key": "AUTO_MARKET_SCHEME_ENABLED", "config_value": "false", "config_type": "boolean", "category": "strategy",
|
||||||
"description": "开启后,crontab 定时运行 scripts/update_market_scheme.py --apply 时自动更新 MARKET_SCHEME(根据 BTC 行情识别牛/熊/正常)。"},
|
"description": "开启后,crontab 定时运行 scripts/update_market_scheme.py --apply 时自动更新 MARKET_SCHEME(根据 BTC 行情识别牛/熊/正常)。"},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -384,7 +384,7 @@ const GlobalConfig = () => {
|
||||||
// 市场状态方案:便于在不同行情间一键切换(熊市/正常/牛市/保守)
|
// 市场状态方案:便于在不同行情间一键切换(熊市/正常/牛市/保守)
|
||||||
scheme_bear: {
|
scheme_bear: {
|
||||||
name: '熊市',
|
name: '熊市',
|
||||||
desc: '【熊市专用】放宽止损5%、单仓≤8%、4H下跌禁止开多、大盘过滤开启。避免逆势抄底、减少回撤。',
|
desc: '【熊市专用】放宽止损5%、单仓≤8%、4H下跌禁止开多、4H上涨禁止开空、大盘过滤开启。避免逆势抄底/做空、减少回撤。',
|
||||||
signatureKeys: ['MARKET_SCHEME'],
|
signatureKeys: ['MARKET_SCHEME'],
|
||||||
configs: {
|
configs: {
|
||||||
MARKET_SCHEME: 'bear',
|
MARKET_SCHEME: 'bear',
|
||||||
|
|
@ -392,12 +392,13 @@ const GlobalConfig = () => {
|
||||||
MAX_POSITION_PERCENT: 0.08,
|
MAX_POSITION_PERCENT: 0.08,
|
||||||
ATR_STOP_LOSS_MULTIPLIER: 2.5,
|
ATR_STOP_LOSS_MULTIPLIER: 2.5,
|
||||||
BLOCK_LONG_WHEN_4H_DOWN: true,
|
BLOCK_LONG_WHEN_4H_DOWN: true,
|
||||||
|
BLOCK_SHORT_WHEN_4H_UP: true,
|
||||||
BETA_FILTER_ENABLED: true,
|
BETA_FILTER_ENABLED: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
scheme_normal: {
|
scheme_normal: {
|
||||||
name: '正常',
|
name: '正常',
|
||||||
desc: '【常规市场】止损3%、单仓12%、4H下跌可开多。适合震荡或温和行情。',
|
desc: '【常规市场】止损3%、单仓12%、4H下跌可开多、4H上涨禁止开空。适合震荡或温和行情。',
|
||||||
signatureKeys: ['MARKET_SCHEME'],
|
signatureKeys: ['MARKET_SCHEME'],
|
||||||
configs: {
|
configs: {
|
||||||
MARKET_SCHEME: 'normal',
|
MARKET_SCHEME: 'normal',
|
||||||
|
|
@ -405,11 +406,12 @@ const GlobalConfig = () => {
|
||||||
MAX_POSITION_PERCENT: 0.12,
|
MAX_POSITION_PERCENT: 0.12,
|
||||||
ATR_STOP_LOSS_MULTIPLIER: 2.5,
|
ATR_STOP_LOSS_MULTIPLIER: 2.5,
|
||||||
BLOCK_LONG_WHEN_4H_DOWN: false,
|
BLOCK_LONG_WHEN_4H_DOWN: false,
|
||||||
|
BLOCK_SHORT_WHEN_4H_UP: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
scheme_bull: {
|
scheme_bull: {
|
||||||
name: '牛市',
|
name: '牛市',
|
||||||
desc: '【牛市专用】止损3%、单仓12%、4H下跌可开多、ATR倍数略低。偏积极。',
|
desc: '【牛市专用】止损3%、单仓12%、4H下跌可开多、4H上涨禁止开空、ATR倍数略低。偏积极。',
|
||||||
signatureKeys: ['MARKET_SCHEME'],
|
signatureKeys: ['MARKET_SCHEME'],
|
||||||
configs: {
|
configs: {
|
||||||
MARKET_SCHEME: 'bull',
|
MARKET_SCHEME: 'bull',
|
||||||
|
|
@ -417,11 +419,12 @@ const GlobalConfig = () => {
|
||||||
MAX_POSITION_PERCENT: 0.12,
|
MAX_POSITION_PERCENT: 0.12,
|
||||||
ATR_STOP_LOSS_MULTIPLIER: 2.0,
|
ATR_STOP_LOSS_MULTIPLIER: 2.0,
|
||||||
BLOCK_LONG_WHEN_4H_DOWN: false,
|
BLOCK_LONG_WHEN_4H_DOWN: false,
|
||||||
|
BLOCK_SHORT_WHEN_4H_UP: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
scheme_conservative: {
|
scheme_conservative: {
|
||||||
name: '保守',
|
name: '保守',
|
||||||
desc: '【极保守】止损6%、单仓6%、4H下跌禁止开多。最小仓位、最严趋势过滤。',
|
desc: '【极保守】止损6%、单仓6%、4H下跌禁止开多、4H上涨禁止开空。最小仓位、最严趋势过滤。',
|
||||||
signatureKeys: ['MARKET_SCHEME'],
|
signatureKeys: ['MARKET_SCHEME'],
|
||||||
configs: {
|
configs: {
|
||||||
MARKET_SCHEME: 'conservative',
|
MARKET_SCHEME: 'conservative',
|
||||||
|
|
@ -429,6 +432,7 @@ const GlobalConfig = () => {
|
||||||
MAX_POSITION_PERCENT: 0.06,
|
MAX_POSITION_PERCENT: 0.06,
|
||||||
ATR_STOP_LOSS_MULTIPLIER: 2.5,
|
ATR_STOP_LOSS_MULTIPLIER: 2.5,
|
||||||
BLOCK_LONG_WHEN_4H_DOWN: true,
|
BLOCK_LONG_WHEN_4H_DOWN: true,
|
||||||
|
BLOCK_SHORT_WHEN_4H_UP: true,
|
||||||
BETA_FILTER_ENABLED: true,
|
BETA_FILTER_ENABLED: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -498,6 +502,7 @@ const GlobalConfig = () => {
|
||||||
SYSTEM_ORDER_ID_PREFIX: { value: 'SYS', type: 'string', category: 'position', description: '系统单标识:下单时写入 newClientOrderId 前缀,同步时仅对「开仓订单 clientOrderId 以此前缀开头」的持仓补建;设空则用「是否有止损止盈单」判断。' },
|
SYSTEM_ORDER_ID_PREFIX: { value: 'SYS', type: 'string', category: 'position', description: '系统单标识:下单时写入 newClientOrderId 前缀,同步时仅对「开仓订单 clientOrderId 以此前缀开头」的持仓补建;设空则用「是否有止损止盈单」判断。' },
|
||||||
MARKET_SCHEME: { value: 'normal', type: 'string', category: 'strategy', description: '市场方案:normal / bear / bull / conservative。切换后自动覆盖止损、仓位、4H趋势过滤等参数。' },
|
MARKET_SCHEME: { value: 'normal', type: 'string', category: 'strategy', description: '市场方案:normal / bear / bull / conservative。切换后自动覆盖止损、仓位、4H趋势过滤等参数。' },
|
||||||
BLOCK_LONG_WHEN_4H_DOWN: { value: false, type: 'boolean', category: 'strategy', description: '4H 趋势下跌时禁止开多。bear / conservative 方案下自动为 true。' },
|
BLOCK_LONG_WHEN_4H_DOWN: { value: false, type: 'boolean', category: 'strategy', description: '4H 趋势下跌时禁止开多。bear / conservative 方案下自动为 true。' },
|
||||||
|
BLOCK_SHORT_WHEN_4H_UP: { value: true, type: 'boolean', category: 'strategy', description: '4H 趋势上涨时禁止开空。默认 true,避免逆势做空导致止损。' },
|
||||||
AUTO_MARKET_SCHEME_ENABLED: { value: false, type: 'boolean', category: 'strategy', description: '开启后,crontab 定时运行 update_market_scheme.py --apply 时自动更新 MARKET_SCHEME(根据 BTC 行情识别牛/熊/正常)。' },
|
AUTO_MARKET_SCHEME_ENABLED: { value: false, type: 'boolean', category: 'strategy', description: '开启后,crontab 定时运行 update_market_scheme.py --apply 时自动更新 MARKET_SCHEME(根据 BTC 行情识别牛/熊/正常)。' },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -660,6 +660,8 @@ class MarketScanner:
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
ema50_4h = None
|
||||||
|
macd_4h = None
|
||||||
use_cached_indicators = False
|
use_cached_indicators = False
|
||||||
if cached_indicators and cached_indicators.get('last_kline_time') == last_kline_time:
|
if cached_indicators and cached_indicators.get('last_kline_time') == last_kline_time:
|
||||||
# 缓存命中,使用缓存的技术指标
|
# 缓存命中,使用缓存的技术指标
|
||||||
|
|
@ -672,6 +674,8 @@ class MarketScanner:
|
||||||
ema20 = cached_indicators.get('ema20')
|
ema20 = cached_indicators.get('ema20')
|
||||||
ema50 = cached_indicators.get('ema50')
|
ema50 = cached_indicators.get('ema50')
|
||||||
ema20_4h = cached_indicators.get('ema20_4h')
|
ema20_4h = cached_indicators.get('ema20_4h')
|
||||||
|
ema50_4h = cached_indicators.get('ema50_4h')
|
||||||
|
macd_4h = cached_indicators.get('macd_4h')
|
||||||
market_regime = cached_indicators.get('marketRegime')
|
market_regime = cached_indicators.get('marketRegime')
|
||||||
else:
|
else:
|
||||||
# 缓存未命中,重新计算技术指标
|
# 缓存未命中,重新计算技术指标
|
||||||
|
|
@ -683,12 +687,14 @@ class MarketScanner:
|
||||||
ema20 = TechnicalIndicators.calculate_ema(close_prices, period=20)
|
ema20 = TechnicalIndicators.calculate_ema(close_prices, period=20)
|
||||||
ema50 = TechnicalIndicators.calculate_ema(close_prices, period=50)
|
ema50 = TechnicalIndicators.calculate_ema(close_prices, period=50)
|
||||||
|
|
||||||
# 计算4H周期的EMA20用于多周期共振检查
|
# 计算4H周期的EMA、MACD用于多周期共振检查
|
||||||
ema20_4h = TechnicalIndicators.calculate_ema(close_prices_4h, period=20) if len(close_prices_4h) >= 20 else None
|
ema20_4h = TechnicalIndicators.calculate_ema(close_prices_4h, period=20) if len(close_prices_4h) >= 20 else None
|
||||||
|
ema50_4h = TechnicalIndicators.calculate_ema(close_prices_4h, period=50) if len(close_prices_4h) >= 50 else None
|
||||||
|
macd_4h = TechnicalIndicators.calculate_macd(close_prices_4h) if len(close_prices_4h) >= 35 else None
|
||||||
|
|
||||||
# 判断市场状态
|
# 判断市场状态
|
||||||
market_regime = TechnicalIndicators.detect_market_regime(close_prices)
|
market_regime = TechnicalIndicators.detect_market_regime(close_prices)
|
||||||
|
|
||||||
# 保存技术指标计算结果到缓存(TTL: 30秒,与K线缓存一致)
|
# 保存技术指标计算结果到缓存(TTL: 30秒,与K线缓存一致)
|
||||||
try:
|
try:
|
||||||
indicators_cache = {
|
indicators_cache = {
|
||||||
|
|
@ -700,6 +706,8 @@ class MarketScanner:
|
||||||
'ema20': ema20,
|
'ema20': ema20,
|
||||||
'ema50': ema50,
|
'ema50': ema50,
|
||||||
'ema20_4h': ema20_4h,
|
'ema20_4h': ema20_4h,
|
||||||
|
'ema50_4h': ema50_4h,
|
||||||
|
'macd_4h': macd_4h,
|
||||||
'marketRegime': market_regime,
|
'marketRegime': market_regime,
|
||||||
}
|
}
|
||||||
await self.client.redis_cache.set(cache_key_indicators, indicators_cache, ttl=30)
|
await self.client.redis_cache.set(cache_key_indicators, indicators_cache, ttl=30)
|
||||||
|
|
@ -749,16 +757,24 @@ class MarketScanner:
|
||||||
|
|
||||||
# 获取4H周期当前价格(用于判断4H趋势)
|
# 获取4H周期当前价格(用于判断4H趋势)
|
||||||
price_4h = close_prices_4h[-1] if len(close_prices_4h) > 0 else current_price
|
price_4h = close_prices_4h[-1] if len(close_prices_4h) > 0 else current_price
|
||||||
|
|
||||||
# 判断4H周期趋势方向
|
# 判断4H周期趋势方向(优先从 Redis 缓存读取,与 strategy 共用)
|
||||||
trend_4h = None
|
try:
|
||||||
if ema20_4h is not None:
|
from .trend_4h_cache import get_trend_4h_cached
|
||||||
if price_4h > ema20_4h:
|
trend_4h = await get_trend_4h_cached(
|
||||||
trend_4h = 'up'
|
self.client.redis_cache if self.client else None,
|
||||||
elif price_4h < ema20_4h:
|
symbol, price_4h, ema20_4h, ema50_4h, macd_4h,
|
||||||
trend_4h = 'down'
|
)
|
||||||
else:
|
except Exception as e:
|
||||||
trend_4h = 'neutral'
|
logger.debug(f"{symbol} trend_4h 缓存获取失败: {e}")
|
||||||
|
trend_4h = None
|
||||||
|
if ema20_4h is not None:
|
||||||
|
if price_4h > ema20_4h:
|
||||||
|
trend_4h = 'up'
|
||||||
|
elif price_4h < ema20_4h:
|
||||||
|
trend_4h = 'down'
|
||||||
|
else:
|
||||||
|
trend_4h = 'neutral'
|
||||||
|
|
||||||
# 策略权重配置(与strategy.py保持一致)
|
# 策略权重配置(与strategy.py保持一致)
|
||||||
TREND_SIGNAL_WEIGHTS = {
|
TREND_SIGNAL_WEIGHTS = {
|
||||||
|
|
@ -881,6 +897,8 @@ class MarketScanner:
|
||||||
'ema20': ema20,
|
'ema20': ema20,
|
||||||
'ema50': ema50,
|
'ema50': ema50,
|
||||||
'ema20_4h': ema20_4h, # 4H周期EMA20,用于多周期共振
|
'ema20_4h': ema20_4h, # 4H周期EMA20,用于多周期共振
|
||||||
|
'ema50_4h': ema50_4h, # 4H周期EMA50
|
||||||
|
'macd_4h': macd_4h, # 4H周期MACD
|
||||||
'price_4h': close_prices_4h[-1] if len(close_prices_4h) > 0 else current_price, # 4H周期当前价格
|
'price_4h': close_prices_4h[-1] if len(close_prices_4h) > 0 else current_price, # 4H周期当前价格
|
||||||
'marketRegime': market_regime,
|
'marketRegime': market_regime,
|
||||||
'signalScore': signal_score, # 保留用于兼容性
|
'signalScore': signal_score, # 保留用于兼容性
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ TTL_KLINES_REST = 1800 # REST 拉取的 K 线默认 30 分钟
|
||||||
TTL_KLINES_REST_OLD = 300 # 旧格式 klines:{s}:{i}:{limit} 默认 5 分钟
|
TTL_KLINES_REST_OLD = 300 # 旧格式 klines:{s}:{i}:{limit} 默认 5 分钟
|
||||||
TTL_LISTEN_KEY = 55 * 60 # 55 分钟(listenKey 缓存)
|
TTL_LISTEN_KEY = 55 * 60 # 55 分钟(listenKey 缓存)
|
||||||
TTL_TREND_STATE = 3600
|
TTL_TREND_STATE = 3600
|
||||||
|
TTL_TREND_4H = 600 # 10 分钟(trend_4h 基于 4H K 线,同根 K 线内变化缓慢)
|
||||||
TTL_INDICATORS = 30
|
TTL_INDICATORS = 30
|
||||||
TTL_RECO_SNAPSHOT = 7200
|
TTL_RECO_SNAPSHOT = 7200
|
||||||
TTL_RECO_ITEM = 3600
|
TTL_RECO_ITEM = 3600
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,13 @@ class TradingStrategy:
|
||||||
f"{symbol} 4H 趋势下跌,禁止开多(BLOCK_LONG_WHEN_4H_DOWN=true),跳过"
|
f"{symbol} 4H 趋势下跌,禁止开多(BLOCK_LONG_WHEN_4H_DOWN=true),跳过"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
# 4H 上涨禁止开空(对称过滤:避免逆势做空,减少上涨行情中空单止损)
|
||||||
|
block_short_4h_up = bool(config.TRADING_CONFIG.get("BLOCK_SHORT_WHEN_4H_UP", True))
|
||||||
|
if block_short_4h_up and trade_direction == "SELL" and trade_signal.get("trend_4h") == "up":
|
||||||
|
logger.info(
|
||||||
|
f"{symbol} 4H 趋势上涨,禁止开空(BLOCK_SHORT_WHEN_4H_UP=true),跳过"
|
||||||
|
)
|
||||||
|
continue
|
||||||
# 开仓前资金费率过滤:避免在费率极端不利于己方时进场
|
# 开仓前资金费率过滤:避免在费率极端不利于己方时进场
|
||||||
if not await self._check_funding_rate_filter(symbol, trade_direction):
|
if not await self._check_funding_rate_filter(symbol, trade_direction):
|
||||||
logger.info(f"{symbol} 资金费率过滤未通过,跳过开仓")
|
logger.info(f"{symbol} 资金费率过滤未通过,跳过开仓")
|
||||||
|
|
@ -552,9 +559,17 @@ class TradingStrategy:
|
||||||
ema20_4h = symbol_info.get('ema20_4h')
|
ema20_4h = symbol_info.get('ema20_4h')
|
||||||
ema50_4h = symbol_info.get('ema50_4h')
|
ema50_4h = symbol_info.get('ema50_4h')
|
||||||
macd_4h = symbol_info.get('macd_4h')
|
macd_4h = symbol_info.get('macd_4h')
|
||||||
|
|
||||||
# 判断4H周期趋势方向(多指标投票)
|
# 优先从 Redis 缓存读取 trend_4h(基于 WS K 线,同根 4H 内减少重复计算)
|
||||||
trend_4h = self._judge_trend_4h(price_4h, ema20_4h, ema50_4h, macd_4h)
|
try:
|
||||||
|
from .trend_4h_cache import get_trend_4h_cached
|
||||||
|
redis_cache = self.client.redis_cache if self.client else None
|
||||||
|
trend_4h = await get_trend_4h_cached(
|
||||||
|
redis_cache, symbol, price_4h, ema20_4h, ema50_4h, macd_4h
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"{symbol} trend_4h 缓存获取失败,回退本地计算: {e}")
|
||||||
|
trend_4h = self._judge_trend_4h(price_4h, ema20_4h, ema50_4h, macd_4h)
|
||||||
|
|
||||||
signal_strength = 0
|
signal_strength = 0
|
||||||
reasons = []
|
reasons = []
|
||||||
|
|
|
||||||
96
trading_system/trend_4h_cache.py
Normal file
96
trading_system/trend_4h_cache.py
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
"""
|
||||||
|
trend_4h 缓存模块
|
||||||
|
|
||||||
|
基于已有 WS K 线缓存,将计算得到的 trend_4h(up/down/neutral)写入 Redis,
|
||||||
|
供 strategy、market_scanner 等复用,减少重复 EMA/MACD 计算。
|
||||||
|
Redis Key: trend_4h:{symbol},TTL 10 分钟。
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
from typing import Optional, Dict, Any
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
KEY_PREFIX = "trend_4h:"
|
||||||
|
try:
|
||||||
|
from .redis_ttl import TTL_TREND_4H
|
||||||
|
except ImportError:
|
||||||
|
TTL_TREND_4H = 600 # 10 分钟
|
||||||
|
|
||||||
|
|
||||||
|
def _judge_trend_4h(
|
||||||
|
price_4h: float,
|
||||||
|
ema20_4h: Optional[float],
|
||||||
|
ema50_4h: Optional[float],
|
||||||
|
macd_4h: Optional[Dict],
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
多指标投票判断 4H 趋势(与 strategy._judge_trend_4h 一致)。
|
||||||
|
Returns: 'up' | 'down' | 'neutral'
|
||||||
|
"""
|
||||||
|
if ema20_4h is None:
|
||||||
|
return 'neutral'
|
||||||
|
score = 0
|
||||||
|
if price_4h > ema20_4h:
|
||||||
|
score += 1
|
||||||
|
elif price_4h < ema20_4h:
|
||||||
|
score -= 1
|
||||||
|
if ema50_4h is not None:
|
||||||
|
if ema20_4h > ema50_4h:
|
||||||
|
score += 1
|
||||||
|
elif ema20_4h < ema50_4h:
|
||||||
|
score -= 1
|
||||||
|
if macd_4h and isinstance(macd_4h, dict):
|
||||||
|
macd_hist = macd_4h.get('histogram', 0)
|
||||||
|
if macd_hist > 0:
|
||||||
|
score += 1
|
||||||
|
elif macd_hist < 0:
|
||||||
|
score -= 1
|
||||||
|
if score >= 2:
|
||||||
|
return 'up'
|
||||||
|
if score <= -2:
|
||||||
|
return 'down'
|
||||||
|
return 'neutral'
|
||||||
|
|
||||||
|
|
||||||
|
async def get_trend_4h_cached(
|
||||||
|
redis_cache: Any,
|
||||||
|
symbol: str,
|
||||||
|
price_4h: float,
|
||||||
|
ema20_4h: Optional[float],
|
||||||
|
ema50_4h: Optional[float],
|
||||||
|
macd_4h: Optional[Dict],
|
||||||
|
ttl_sec: int = None,
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
优先从 Redis 读取 trend_4h,未命中则计算并写入缓存。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
redis_cache: RedisCache 实例(可为 None,则直接计算不缓存)
|
||||||
|
symbol: 交易对
|
||||||
|
price_4h, ema20_4h, ema50_4h, macd_4h: 计算 trend_4h 所需输入
|
||||||
|
ttl_sec: 缓存 TTL,默认使用 redis_ttl.TTL_TREND_4H(10 分钟)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
'up' | 'down' | 'neutral'
|
||||||
|
"""
|
||||||
|
if ttl_sec is None:
|
||||||
|
ttl_sec = TTL_TREND_4H
|
||||||
|
key = f"{KEY_PREFIX}{symbol}"
|
||||||
|
if redis_cache:
|
||||||
|
try:
|
||||||
|
await redis_cache.connect()
|
||||||
|
cached = await redis_cache.get(key)
|
||||||
|
if cached and cached in ('up', 'down', 'neutral'):
|
||||||
|
return cached
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"trend_4h 缓存读取失败 {symbol}: {e}")
|
||||||
|
|
||||||
|
trend = _judge_trend_4h(price_4h, ema20_4h, ema50_4h, macd_4h)
|
||||||
|
|
||||||
|
if redis_cache:
|
||||||
|
try:
|
||||||
|
await redis_cache.set(key, trend, ttl=ttl_sec)
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"trend_4h 缓存写入失败 {symbol}: {e}")
|
||||||
|
|
||||||
|
return trend
|
||||||
Loading…
Reference in New Issue
Block a user