auto_trade_sys/trading_system/config.py
薇薇安 5d5ead36ac feat(strategy): Implement stagnation early exit strategy configuration and logic
Added new configuration options for the stagnation early exit strategy, allowing for partial position closure and stop-loss adjustments when a position has not reached a new high after a specified period. Updated relevant backend logic to handle this strategy, including tracking maximum profit and timestamps for the last new high. Frontend components were also updated to reflect these new settings and their descriptions, enhancing user control over trading strategies.
2026-02-27 23:48:05 +08:00

498 lines
31 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
配置文件 - API密钥和交易参数配置
支持从数据库读取配置(优先),回退到环境变量和默认值
支持动态重载配置
"""
import os
from typing import Optional
from pathlib import Path
# 加载 .env 文件(优先从 trading_system/.env其次从 backend/.env再到项目根目录/.env
# 重要:对于 ATS_ACCOUNT_ID如果 supervisor 已设置(交易进程),则不要从 .env 覆盖
# .env 中的 ATS_ACCOUNT_ID 只用于 recommendations_main.py 的默认值
try:
from dotenv import load_dotenv
trading_system_dir = Path(__file__).parent
project_root = trading_system_dir.parent
backend_dir = project_root / "backend"
env_files = [
trading_system_dir / '.env', # trading_system/.env
backend_dir / '.env', # backend/.env线上常见放这里DB/REDIS/ATS_MASTER_KEY
project_root / '.env', # 项目根目录/.env
]
# 先保存 supervisor 设置的 ATS_ACCOUNT_ID如果存在
# supervisor 在启动交易进程时会设置这个环境变量,优先级应该高于 .env
supervisor_account_id = os.getenv("ATS_ACCOUNT_ID")
for env_file in env_files:
if env_file.exists():
# 加载 .env 文件(会覆盖环境变量)
load_dotenv(env_file, override=True)
# logger.debug(f"[config.py] 已加载 .env 文件: {env_file}") # 调试信息,已禁用
# 如果 supervisor 设置了 ATS_ACCOUNT_ID恢复它交易进程使用 supervisor 的值,不依赖 .env
if supervisor_account_id:
os.environ["ATS_ACCOUNT_ID"] = supervisor_account_id
# logger.debug(f"[config.py] 恢复 supervisor 设置的 ATS_ACCOUNT_ID={supervisor_account_id}(交易进程使用此值,不依赖 .env") # 调试信息,已禁用
break
else:
raise Exception("supervisor 未设置 ATS_ACCOUNT_ID")
# # 如果都不存在,尝试加载但不报错
# load_dotenv(project_root / '.env', override=False)
# # 同样恢复 supervisor 设置的值
# if supervisor_account_id:
# os.environ["ATS_ACCOUNT_ID"] = supervisor_account_id
except ImportError:
# python-dotenv 未安装时忽略
pass
except Exception:
# 加载 .env 文件失败时忽略,不影响程序运行
# logger.warning(f"[config.py] 加载ATS_ACCOUNT_ID时出错: {e}") # 调试信息,已禁用
pass # ⚠️ 2026-01-27修复添加pass语句修复IndentationError
# 尝试从数据库加载配置
USE_DB_CONFIG = False
_config_manager = None
def _init_config_manager():
"""初始化配置管理器"""
global USE_DB_CONFIG, _config_manager
if _config_manager is not None:
return _config_manager
# ⚠️ 2026-01-27修复移除所有print语句避免污染日志格式
# 只在错误时使用stderr输出正常初始化过程静默处理
import sys
from pathlib import Path
try:
# 从trading_system目录向上两级到项目根目录然后找backend
project_root = Path(__file__).parent.parent
backend_path = project_root / 'backend'
if not backend_path.exists():
# 只在错误时输出到stderr不影响正常日志
print(f"[配置管理器] ❌ backend目录不存在: {backend_path}", file=sys.stderr)
USE_DB_CONFIG = False
return None
# 检查config_manager.py是否存在
config_manager_file = backend_path / 'config_manager.py'
if not config_manager_file.exists():
print(f"[配置管理器] ❌ config_manager.py不存在: {config_manager_file}", file=sys.stderr)
USE_DB_CONFIG = False
return None
# 添加到sys.path
backend_str = str(backend_path)
if backend_str not in sys.path:
sys.path.insert(0, backend_str)
# 尝试导入
try:
from config_manager import ConfigManager # type: ignore
# 从环境变量获取 account_idtrading_system 进程通过 ATS_ACCOUNT_ID 指定)
try:
account_id = int(os.getenv("ATS_ACCOUNT_ID") or os.getenv("ACCOUNT_ID") or 1)
except Exception:
account_id = 1
# 为当前账号创建 ConfigManager 实例(而不是使用全局的)
config_manager = ConfigManager.for_account(account_id)
# 测试数据库连接
try:
config_manager.reload()
# 成功时使用logger输出此时日志系统应该已初始化
import logging
try:
logger = logging.getLogger(__name__)
logger.info(f"[配置管理器] ✓ 数据库连接成功,已加载 {len(config_manager._cache)} 个配置项")
logger.info("[配置管理器] ✓ 配置管理器初始化成功,将从数据库读取配置")
except Exception:
# 如果日志系统还未初始化,静默处理
pass
_config_manager = config_manager
USE_DB_CONFIG = True
return config_manager
except Exception as db_error:
# 错误时输出到stderr不影响正常日志
print(f"[配置管理器] ⚠ 数据库连接失败: {db_error}", file=sys.stderr)
USE_DB_CONFIG = False
return None
except ImportError as e:
print(f"[配置管理器] ❌ 无法导入config_manager: {e}", file=sys.stderr)
USE_DB_CONFIG = False
return None
except Exception as e:
import traceback
print(f"[配置管理器] ❌ 配置管理器初始化失败: {e}", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
USE_DB_CONFIG = False
return None
except Exception as e:
import traceback
print(f"[配置管理器] ❌ 初始化异常: {e}", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
USE_DB_CONFIG = False
return None
# 初始化配置管理器(在模块加载时执行)
# ⚠️ 2026-01-27修复静默初始化只在错误时输出到stderr避免污染日志格式
try:
_init_config_manager()
except Exception as e:
# 错误时输出到stderr不影响正常日志
import sys
import traceback
print(f"[config.py] 配置管理器初始化异常: {e}", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
def _get_config_value(key, default=None):
"""获取配置值(支持动态重载)"""
if _config_manager:
try:
# 不每次都reload避免性能问题只在需要时reload
value = _config_manager.get(key)
# 如果从数据库获取到值直接返回排除None和空字符串除非是敏感配置
if value is not None:
# 对于API密钥即使是空字符串也返回表示数据库中有配置但值为空
if key in ['BINANCE_API_KEY', 'BINANCE_API_SECRET']:
return value if value else default
# 对于其他配置空字符串视为无效返回default
if value == '':
return default
return value
except Exception as e:
import logging
logger = logging.getLogger(__name__)
logger.debug(f"从配置管理器获取{key}失败: {e},尝试环境变量")
# 回退到环境变量
env_value = os.getenv(key)
if env_value is not None and env_value != '':
return env_value
# 最后返回默认值
return default
# 默认配置字典偏向山寨短线、适时落袋4H 中性可开单、大盘过滤略放宽、信号门槛适中)
DEFAULT_TRADING_CONFIG = {
# ===== 用户风险旋钮(山寨币专属策略)=====
'AUTO_TRADE_ENABLED': True, # 自动交易总开关
'MAX_OPEN_POSITIONS': 4, # 同时持仓数量上限总仓位12% / 单笔1.5% = 最多4个
'MAX_DAILY_ENTRIES': 15, # 每日最多15笔快速验证模式提高上限以快速验证策略
'ONE_WAY_POSITION_ONLY': True, # 账号固定为单向持仓模式,不传 positionSide不检测对冲模式避免 -4061
'MAX_POSITION_PERCENT': 0.20, # 单笔仓位上限20%(作为风控熔断,实际仓位由固定风险模型决定)
'MAX_TOTAL_POSITION_PERCENT': 0.80, # 总仓位80%(避免满仓,留有余地)
'MIN_POSITION_PERCENT': 0.01, # 最小仓位1%
'MIN_MARGIN_USDT': 2.0, # 最小保证金2美元降低门槛允许高杠杆小资金交易避免被 risk_manager 拦截)
'MIN_CHANGE_PERCENT': 0.5, # 最小价格变动0.5%
'TOP_N_SYMBOLS': 30, # 扩大候选池到30个增加机会
'SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT': 20, # 智能补单候选增加
'MAX_SCAN_SYMBOLS': 500, # 扫描前500个
'EXCLUDE_MAJOR_COINS': True, # 排除主流币
'STOP_LOSS_PERCENT': 0.05, # 基础止损5%配合ATR动态止损作为保底
'TAKE_PROFIT_PERCENT': 0.25, # 第二目标止盈25%(略拉近止盈以提高触达率)
'TAKE_PROFIT_1_PERCENT': 0.12, # 第一目标止盈12%(山寨币不必久拿,早点落袋为安,降低反转风险)
'MIN_RR_FOR_TP1': 1.2, # 第一目标止盈的最小盈亏比(相对于止损距离),略放宽以提高触发率
'MIN_STOP_LOSS_PRICE_PCT': 0.03, # 最小止损价格变动3%(适当放宽止损,减少正常波动被扫)
'MIN_TAKE_PROFIT_PRICE_PCT': 0.02, # 最小止盈价格变动2%
'USE_ATR_STOP_LOSS': True, # 使用ATR动态止损
'ATR_STOP_LOSS_MULTIPLIER': 2.0, # ATR止损倍数2.0(适中止损宽度,兼顾胜率与回撤)
'ATR_TAKE_PROFIT_MULTIPLIER': 1.5, # ATR止盈倍数1.5(配合适中盈亏比,更容易触及止盈)
'RISK_REWARD_RATIO': 1.5, # 盈亏比1.5:1在当前波动水平下更实际可达
'USE_MARGIN_CAP_FOR_TP': True, # 止盈按保证金上限封顶,避免 TP 过远难以触及、亏损扛单。勿关。
'USE_MARGIN_CAP_FOR_SL': True, # 止损按保证金上限封顶,避免 ATR 过宽导致 -60%-80% 保证金扛单。勿关。
'ATR_PERIOD': 14, # ATR计算周期14
'USE_DYNAMIC_ATR_MULTIPLIER': False, # 不使用动态ATR
'ATR_MULTIPLIER_MIN': 1.5, # 动态ATR倍数最小值回归盈利期设置
'ATR_MULTIPLIER_MAX': 2.5, # 动态ATR倍数最大值回归盈利期设置
'SCAN_INTERVAL': 900, # 扫描间隔15分钟900秒快速验证模式提高扫描频率以增加交易机会
'SCAN_SYMBOL_ANALYSIS_TIMEOUT_SEC': 12, # 单个交易对「详细分析」超时(秒);已并行拉取主周期/确认周期K线12秒通常够用网络慢可调大18~25
'SCAN_KLINE_FETCH_TIMEOUT_SEC': 8, # K线拉取单独超时超时则返回降级结果仅涨跌幅/成交量),不拖满整分析超时
'SCAN_KLINE_LIMIT': 50, # 扫描用 K 线根数(主周期 4h 时 MACD 需≥35、EMA50/市场状态需≥50否则信号恒为 0/unknown
'SCAN_PREFER_WEBSOCKET': True, # 优先使用 WebSocket 方式:如果缓存不完整,等待 WebSocket 推送数据最多1-1.5秒),而不是立即回退 REST API
'SCAN_LIMIT_KLINE_SUBSCRIBE': True, # 限制 K 线订阅:只在缓存完全没有数据时才订阅,避免订阅过多导致负载上升
'SCAN_PREWARM_KLINE_ENABLED': True, # 扫描前预热 K 线:批量预订阅 WS + REST 预取,提高缓存命中率、减少分析超时
'SCAN_PREWARM_CONCURRENT': 5, # 预热阶段同时预取多少个交易对的 K 线(避免并发过高)
# 多账号/低配服务器(如 2 CPU 4G降低并发与错峰扫描避免 CPU 打满
'SCAN_CONCURRENT_SYMBOLS': 2, # 扫描时同时分析多少个交易对2 CPU 4G 多账号建议 2单账号可 35
'SCAN_STAGGER_BY_ACCOUNT': False, # 多账号错峰开关:单账号可关闭;多账号同时跑时改为 True 避免同时扫
'SCAN_STAGGER_RANDOM': True, # True=随机延迟(更分散)False=按 account_id 固定步长延迟
'SCAN_STAGGER_MIN_SEC': 10, # 随机延迟下限(秒)
'SCAN_STAGGER_MAX_SEC': 120, # 随机延迟上限(秒)
'SCAN_STAGGER_SEC': 60, # 非随机模式下每账号步长account_id-1 * 此值)
'KLINE_INTERVAL': '1h',
'PRIMARY_INTERVAL': '4h', # 主周期4小时过滤噪音
'CONFIRM_INTERVAL': '1d', # 确认周期日线,看大趋势
'ENTRY_INTERVAL': '1h', # 入场周期1小时避免太小的时间框架
# 入场短周期用于快速方向确认例如15m不要太小以免噪音太大
'ENTRY_SHORT_INTERVAL': '15m',
# 是否开启“短周期方向过滤”避免在15m明显上涨时做空、明显下跌时做多快速验证模式关闭以增加交易机会
'ENTRY_SHORT_TREND_FILTER_ENABLED': True,
# 短周期方向过滤使用的最小趋势幅度例如0.003=0.3%),变化太小视为震荡不过滤
'ENTRY_SHORT_TREND_MIN_PCT': 0.003,
# 检查最近多少根短周期K线来评估方向例如3根15m约等于45分钟
'ENTRY_SHORT_CONFIRM_CANDLES': 3,
'MIN_VOLUME_24H': 30000000, # 24小时成交额≥3000万美元过滤垃圾币
'MIN_VOLUME_24H_STRICT': 50000000, # 严格过滤≥5000万美元
'MIN_VOLATILITY': 0.03, # 最小波动率3%,过滤死币
'MIN_SIGNAL_STRENGTH': 7, # 信号强度≥7山寨短线适度放宽以多抓机会配合扫描器回退弱信号偏保守可改为8
# ===== 资金费率过滤(开仓前)=====
'FUNDING_RATE_FILTER_ENABLED': True, # 开仓前检查资金费率,避免在费率极端不利于己方时进场
'MAX_FUNDING_RATE_FOR_LONG': 0.001, # 做多时 lastFundingRate 超过此值则跳过0.1%=多头付费高)
'MIN_FUNDING_RATE_FOR_SHORT': -0.001, # 做空时 lastFundingRate 低于此值则跳过(-0.1%=空头付费高)
# 扫描阶段资金费率过滤:排除 |lastFundingRate| 过大的标的,避免进入 TOP_N
'SCAN_FUNDING_RATE_FILTER_ENABLED': True,
'SCAN_FUNDING_RATE_MAX_ABS': 0.001, # 绝对值超过 0.1% 则从扫描候选剔除
# ===== 市场节奏自动识别(低波动期 vs 正常期,自动切换参数,无需手动)=====
'MARKET_REGIME_AUTO': True, # 是否根据波动率自动识别并切换低波动期参数
'LOW_VOLATILITY_THRESHOLD': 2.5, # 24h |changePercent| 中位数低于此值判为低波动期(%
'LOW_VOLATILITY_MAX_POSITION_PERCENT': 0.15, # 低波动期单币最大仓位(正常期用 MAX_POSITION_PERCENT
'LOW_VOLATILITY_MIN_SIGNAL_STRENGTH': 9, # 低波动期信号强度门槛(更严)
'LOW_VOLATILITY_SCAN_FUNDING_RATE_MAX_ABS': 0.0005, # 低波动期扫描阶段资金费率更严0.05%
'TIME_STOP_MAX_HOLD_HOURS': 8, # 持仓超过 N 小时且未达最小盈利则考虑平仓(低波动期更适用)
'TIME_STOP_MIN_PNL_PCT_TO_HOLD': 0.0, # 持仓超过时间阈值时,若 pnl% 低于此值则平仓0=允许小亏即走)
'TIME_STOP_ENABLED': True, # 是否启用时间/无盈利止损(可与 regime 结合:仅低波动期或始终启用)
# 山寨币早止盈:盈利达标且持仓满 N 小时则市价止盈,不等挂单 TP避免反转回吐
'EARLY_TAKE_PROFIT_ENABLED': True, # 是否启用「盈利达标+持仓时长」早止盈
'EARLY_TAKE_PROFIT_MIN_PNL_PCT': 10.0, # 盈利达到此比例(% of margin即符合条件默认 10%
'EARLY_TAKE_PROFIT_MIN_HOLD_HOURS': 2.0, # 且持仓至少 N 小时才触发(避免刚开仓就平)
# 滞涨早止盈:曾涨到约 N% 后 X 小时内未创新高则分批减仓+抬止损(动能衰减)
'STAGNATION_EARLY_EXIT_ENABLED': False, # 是否启用「滞涨早止盈」(默认关,按需开启)
'STAGNATION_MIN_RUNUP_PCT': 10.0, # 曾达到的最低浮盈%(基于保证金)才考虑滞涨,例如 10 表示 10%
'STAGNATION_NO_NEW_HIGH_HOURS': 3.0, # 多少小时内未创新高视为滞涨,例如 24
'STAGNATION_PARTIAL_CLOSE_PCT': 0.5, # 触发时先平掉仓位比例,例如 0.5=50%
'STAGNATION_LOCK_PCT': 5.0, # 剩余仓位止损上移锁住的利润%(与「最高浮盈的一半」取大)
# 开仓前流动性检查(价差/深度):低量期可减少滑点与冲击成本
'LIQUIDITY_CHECK_BEFORE_ENTRY': False, # 默认关闭;开启后开仓前检查盘口价差与深度,不通过则跳过
'LIQUIDITY_MAX_SPREAD_PCT': 0.005, # 买一卖一价差超过此比例0.5%)则跳过
'LIQUIDITY_MIN_DEPTH_USDT': 30000, # 前 5 档买卖深度合计名义价值低于此值USDT则跳过
# 主动买卖量过滤(可选):与方向一致时通过,极端反向时跳过
'TAKER_RATIO_FILTER_ENABLED': False, # 默认关闭,开启后做多要求 buySellRatio≥下限、做空要求≤上限
'MIN_TAKER_BUY_RATIO_FOR_LONG': 0.85, # 做多时最近1h buySellRatio 低于此值则跳过(主动买不足)
'MAX_TAKER_BUY_RATIO_FOR_SHORT': 1.15, # 做空时最近1h buySellRatio 高于此值则跳过(主动卖不足)
# ===== 动态过滤优化 =====
'BETA_FILTER_ENABLED': True, # 大盘共振过滤BTC/ETH下跌时屏蔽多单山寨常先于大盘动可关或放宽
'BETA_FILTER_THRESHOLD': -0.01, # -1%(默认放宽:仅 15m 跌超 1% 才屏蔽多单,减少错失山寨机会;保守可改为 -0.005
# ===== RSI / 24h 涨跌幅过滤(避免追高杀跌)=====
'MAX_RSI_FOR_LONG': 65, # 做多时 RSI 超过此值则不开多2026-02-1265 避免追高)
'MAX_CHANGE_PERCENT_FOR_LONG': 25, # 做多时 24h 涨跌幅超过此值则不开多(避免追大涨)
'MIN_RSI_FOR_SHORT': 30, # 做空时 RSI 低于此值则不做空(避免深超卖反弹)
'MAX_CHANGE_PERCENT_FOR_SHORT': 10, # 做空时 24h 涨跌幅超过此值则不做空24h 仍大涨时不做空)
# ===== RSI 极限反转策略(抄底/逃顶)=====
'RSI_EXTREME_REVERSE_ENABLED': False, # 关闭RSI极限反转与盈利期一致开启易在趋势里逆势导致止损
'RSI_EXTREME_REVERSE_ONLY_NEUTRAL_4H': True, # 若将来开启反向仅允许4H中性时
'ATR_SPIKE_THRESHOLD': 2.0, # ATR异常激增阈值当前ATR / 平均ATR
'SIGNAL_STRENGTH_POSITION_MULTIPLIER': {7: 0.8, 8: 0.9, 9: 1.0, 10: 1.1}, # 信号强度分级7分80%8分90%9分100%10分110%(适度加仓)
# ===== 仓位管理优化(山寨币专属)=====
'USE_FIXED_RISK_SIZING': True, # 固定每笔风险,避免亏损扩大
'FIXED_RISK_PERCENT': 0.01, # 每笔最多亏总资金1%(回归盈利期风控水平)
'MAX_LEVERAGE_SMALL_CAP': 4, # 高波动/小众币最大杠杆(回归盈利期设置)
'MIN_LEVERAGE': 4, # 动态杠杆下限,允许在风险较大时降低杠杆
'ATR_LEVERAGE_REDUCTION_THRESHOLD': 0.05, # ATR超过5%时降低杠杆
'LEVERAGE': 4, # 基础杠杆(回归盈利期设置)
'USE_DYNAMIC_LEVERAGE': True, # 开启动态杠杆(基于止损宽度自动调整)
'MAX_SINGLE_TRADE_LOSS_PERCENT': 20.0, # 单笔交易最大本金亏损率20%),用于限制杠杆
'MAX_LEVERAGE': 12, # 最大杠杆(回归盈利期上限)
# 移动止损与保本:全局总开关(关闭后不执行移动止损、不执行盈利保本)
'PROFIT_PROTECTION_ENABLED': True, # True=启用移动止损与保本False=全部关闭
'USE_TRAILING_STOP': True, # 在 PROFIT_PROTECTION_ENABLED 为 True 时生效
'TRAILING_STOP_ACTIVATION': 0.10, # 盈利10%后激活(回归盈利期更早锁盈设置)
'TRAILING_STOP_PROTECT': 0.02, # 保护2%利润(回归盈利期紧凑保护)
# 盈利保护:保本含手续费,避免“保本”实为小亏(在 PROFIT_PROTECTION_ENABLED 为 True 时生效)
'FEE_BUFFER_PCT': 0.0015, # 保本价相对入场价缓冲0.15% 覆盖开平双向手续费)
'LOCK_PROFIT_AT_BREAKEVEN_AFTER_PCT': 0.03, # 盈利达3%保证金时移至保本建议3%5%平衡「少亏」与「拿住趋势」0=关闭)
# 最小持仓时间锁立即取消山寨币30分钟可能暴涨暴跌50%
'MIN_HOLD_TIME_SEC': 0, # 取消持仓时间锁
'POSITION_SYNC_INTERVAL': 60, # 持仓状态同步间隔60秒
# ===== 自动交易过滤(用于提升胜率/控频)=====
# 是否仅在 marketRegime=trending 时才自动交易;否则只生成推荐
'AUTO_TRADE_ONLY_TRENDING': True,
# 是否允许 4H 趋势为 neutral 时自动交易山寨短线默认开4H 中性也开单以多抓机会;偏保守可改为 False
'AUTO_TRADE_ALLOW_4H_NEUTRAL': True,
# ===== 趋势入场过滤(防止追在半山腰)=====
# 是否启用基于趋势状态的入场过滤:
# - 扫描阶段会为每个强信号交易对写入一份“趋势状态”到缓存Redis
# - 开仓时根据当前价格相对于信号价格的偏移,过滤“过晚追价”的入场
'USE_TREND_ENTRY_FILTER': True,
# 在信号方向上允许的最大累计趋势幅度(相对于信号价),超过则认为“时机太晚”,不再入场
# 例如0.08 表示价格沿趋势方向已经走了 8% 以上还没上车,则跳过本轮机会(快速验证模式:放宽阈值以增加交易机会)
'MAX_TREND_MOVE_BEFORE_ENTRY': 0.04, # 2026-01-29优化从0.05收紧到0.04,更严格避免追高杀跌
# 趋势状态缓存的 TTL用于控制一轮趋势的“有效期”
'TREND_STATE_TTL_SEC': 3600,
# ===== 推荐系统优化(优先推荐“入场时机不算太晚”的趋势单)=====
# 是否在生成推荐时启用趋势入场时机过滤(仅对趋势市场的顺势推荐生效)
# - 会对比当前价与趋势信号价的偏离,如果已经沿趋势方向走得太远,则不再生成该推荐
'RECO_USE_TREND_ENTRY_FILTER': True,
# 推荐系统使用的最大允许趋势幅度(相对于趋势信号价),默认与自动交易相同或略微更严格
'RECO_MAX_TREND_MOVE_BEFORE_ENTRY': 0.04,
# ===== 智能入场方案C=====
# 根治方案:默认关闭。关闭后回归“纯限价单模式”(不追价/不市价兜底/未成交撤单跳过)
'SMART_ENTRY_ENABLED': True, # 开启智能入场,提高成交率
'SMART_ENTRY_STRONG_SIGNAL': 7, # 强信号阈值(与 MIN_SIGNAL_STRENGTH 一致,便于智能入场触发)
'ENTRY_SYMBOL_COOLDOWN_SEC': 1800, # 同一币种冷却30分钟1800秒快速验证模式缩短冷却以增加交易频率
# ===== 同一交易对连续亏损过滤(避免连续亏损后继续交易)=====
'SYMBOL_LOSS_COOLDOWN_ENABLED': True, # 是否启用同一交易对连续亏损后的冷却
'SYMBOL_MAX_CONSECUTIVE_LOSSES': 2, # 最大允许连续亏损次数(超过则禁止交易)
'SYMBOL_LOSS_COOLDOWN_SEC': 3600, # 连续亏损后的冷却时间默认1小时
'ENTRY_TIMEOUT_SEC': 180, # 智能入场总预算(秒)(限价/追价逻辑内部使用)
'ENTRY_STEP_WAIT_SEC': 15, # 每步等待成交时间(秒)
'ENTRY_CHASE_MAX_STEPS': 4, # 最多追价步数(逐步减少 offset
'ENTRY_MARKET_FALLBACK_AFTER_SEC': 45, # 趋势强时:超过该秒数仍未成交 -> 评估是否市价兜底
'ENTRY_CONFIRM_TIMEOUT_SEC': 30, # 下单后最终确认成交的等待时间(秒)
'ENTRY_MAX_DRIFT_PCT_TRENDING': 0.8, # 追价偏离放宽到0.8%(山寨币跳空大)
'ENTRY_MAX_DRIFT_PCT_RANGING': 0.3, # 震荡/弱趋势时允许的最大追价偏离
'LIMIT_ORDER_OFFSET_PCT': 0.5, # 限价单偏移百分比默认0.5%
'MIN_HOLD_TIME_SEC': 0, # 默认30分钟1800秒已取消
# 持仓监控详细日志:用于排查问题时观察每次检查的当前价/目标价/ROE 等
'POSITION_DETAILED_LOG_ENABLED': False,
# ===== 市场方案与多空过滤(与 MARKET_SCHEME 配合)=====
# 牛市不推空单、熊市不推多单(由 config_manager 根据 MARKET_SCHEME 覆盖)
'BLOCK_SHORT_WHEN_BULL_MARKET': True, # 市场方案=牛市时禁止开空
'BLOCK_LONG_WHEN_BEAR_MARKET': True, # 市场方案=熊市时禁止开多
# ===== 系统单标识(用于同步时区分本系统开仓 vs 手动开仓)=====
# 下单时写入 newClientOrderId = SYSTEM_ORDER_ID_PREFIX_时间戳_随机同步/补建时根据订单 clientOrderId 前缀判断是否系统单
'SYSTEM_ORDER_ID_PREFIX': 'SYS',
# ===== 仅币安有仓时的监管策略 =====
# 当 SYNC_CREATE_MANUAL_ENTRY_RECORD=False 时,仍对「仅币安有仓且存在止损/止盈条件单」的持仓接入监控并补建记录(多为系统单)
'SYNC_MONITOR_BINANCE_POSITIONS_WITH_SLTP': True,
}
def _get_trading_config():
"""获取交易配置支持动态重载优先从Redis读取最新值"""
config = DEFAULT_TRADING_CONFIG.copy()
if _config_manager:
# 从Redis重新加载配置轻量级只从Redis读取不查数据库
# 注意如果Redis不可用或没有数据reload_from_redis()会保持现有缓存,不会触发数据库加载
_config_manager.reload_from_redis()
db_config = _config_manager.get_trading_config()
if db_config:
# 使用数据库配置覆盖默认配置
config.update(db_config)
return config
return config
# 币安API配置优先从数据库回退到环境变量和默认值
# 注意:在模块加载时,配置管理器可能还未初始化完成,所以先使用默认值
# 在main.py中会调用reload_config()重新加载
BINANCE_API_KEY: Optional[str] = _get_config_value('BINANCE_API_KEY', 'your_api_key_here')
BINANCE_API_SECRET: Optional[str] = _get_config_value('BINANCE_API_SECRET', 'your_api_secret_here')
USE_TESTNET: bool = _get_config_value('USE_TESTNET', False) if _get_config_value('USE_TESTNET') is not None else os.getenv('USE_TESTNET', 'False').lower() == 'true'
# 交易参数配置(优先从数据库读取,支持动态重载)
TRADING_CONFIG = _get_trading_config()
# 确保包含所有必要的默认值
for key, value in DEFAULT_TRADING_CONFIG.items():
if key not in TRADING_CONFIG:
TRADING_CONFIG[key] = value
def get_effective_config(key: str, default=None):
"""
按当前市场节奏返回有效配置:若 MARKET_REGIME_AUTO 且为低波动期,
则返回 LOW_VOLATILITY_<key>(若存在),否则返回 TRADING_CONFIG[key]。
用于仓位、信号门槛、资金费率阈值、时间止损等,实现自动把握市场节奏。
"""
try:
from . import market_regime
return market_regime.get_effective_config(key, default)
except ImportError:
return TRADING_CONFIG.get(key, default)
# 提供一个函数来重新加载配置
def reload_config():
"""重新加载配置(供外部调用)"""
global TRADING_CONFIG, BINANCE_API_KEY, BINANCE_API_SECRET, USE_TESTNET, _config_manager, USE_DB_CONFIG
global REDIS_URL, REDIS_USE_TLS, REDIS_SSL_CERT_REQS, REDIS_SSL_CA_CERTS, REDIS_USERNAME, REDIS_PASSWORD, REDIS_SELECT
# 如果配置管理器不存在,尝试初始化
if _config_manager is None:
_init_config_manager()
# 如果配置管理器存在,重新加载
if _config_manager:
try:
_config_manager.reload()
USE_DB_CONFIG = True
except Exception as e:
import logging
logger = logging.getLogger(__name__)
logger.warning(f"重新加载配置失败: {e}")
USE_DB_CONFIG = False
# 重新获取API密钥优先从配置管理器
new_api_key = _get_config_value('BINANCE_API_KEY', 'your_api_key_here')
new_api_secret = _get_config_value('BINANCE_API_SECRET', 'your_api_secret_here')
# 如果获取到有效值,更新全局变量
if new_api_key and new_api_key != 'your_api_key_here':
BINANCE_API_KEY = new_api_key
if new_api_secret and new_api_secret != 'your_api_secret_here':
BINANCE_API_SECRET = new_api_secret
USE_TESTNET = _get_config_value('USE_TESTNET', False) if _get_config_value('USE_TESTNET') is not None else os.getenv('USE_TESTNET', 'False').lower() == 'true'
TRADING_CONFIG = _get_trading_config()
# 重新加载 Redis 配置
REDIS_URL = _get_config_value('REDIS_URL', os.getenv('REDIS_URL', REDIS_URL))
REDIS_USE_TLS = _get_config_value('REDIS_USE_TLS', False) if _get_config_value('REDIS_USE_TLS') is not None else os.getenv('REDIS_USE_TLS', 'False').lower() == 'true'
REDIS_SSL_CERT_REQS = _get_config_value('REDIS_SSL_CERT_REQS', REDIS_SSL_CERT_REQS)
REDIS_SSL_CA_CERTS = _get_config_value('REDIS_SSL_CA_CERTS', REDIS_SSL_CA_CERTS)
REDIS_USERNAME = _get_config_value('REDIS_USERNAME', os.getenv('REDIS_USERNAME', REDIS_USERNAME))
REDIS_PASSWORD = _get_config_value('REDIS_PASSWORD', os.getenv('REDIS_PASSWORD', REDIS_PASSWORD))
REDIS_SELECT = _get_config_value('REDIS_SELECT', os.getenv('REDIS_SELECT', REDIS_SELECT))
# 确保默认值
for key, value in DEFAULT_TRADING_CONFIG.items():
if key not in TRADING_CONFIG:
TRADING_CONFIG[key] = value
# 连接配置
CONNECTION_TIMEOUT = int(os.getenv('CONNECTION_TIMEOUT', '30')) # 连接超时时间(秒)
CONNECTION_RETRIES = int(os.getenv('CONNECTION_RETRIES', '3')) # 连接重试次数
# 仅用于 get_open_positions / get_recent_trades 等只读接口的单次等待时间,不影响下单/止损止盈的快速失败
# 调大此值会延长单次请求最大等待时间,在同步/查询持仓时可能阻塞事件循环,影响实时性;保持 60 秒,通过增加重试+退避应对偶发超时
READ_ONLY_REQUEST_TIMEOUT = int(os.getenv('READ_ONLY_REQUEST_TIMEOUT', '60'))
# 创建 Algo 条件单(止损/止盈)单次请求超时(秒),币安 Algo 接口在高负载时易超时;可由 TRADING_CONFIG['ALGO_ORDER_TIMEOUT_SEC'] 或环境变量覆盖
ALGO_ORDER_TIMEOUT_SEC = int(os.getenv('ALGO_ORDER_TIMEOUT_SEC', '45'))
# 获取持仓时过滤掉名义价值低于此值的仓位USDT与币安仪表板不一致时可调低或设为 0
POSITION_MIN_NOTIONAL_USDT = float(os.getenv('POSITION_MIN_NOTIONAL_USDT', '1.0'))
# 市场 WS 多进程共用:有 Redis 时默认开启,仅 Leader 进程建连接,其余从 Redis 读
USE_SHARED_MARKET_WS = os.getenv('USE_SHARED_MARKET_WS', 'true').lower() in ('1', 'true', 'yes')
# Redis 缓存配置(优先从数据库,回退到环境变量和默认值)
REDIS_URL = _get_config_value('REDIS_URL', os.getenv('REDIS_URL', 'redis://localhost:6379'))
REDIS_USE_TLS = _get_config_value('REDIS_USE_TLS', False) if _get_config_value('REDIS_USE_TLS') is not None else os.getenv('REDIS_USE_TLS', 'False').lower() == 'true'
REDIS_SSL_CERT_REQS = _get_config_value('REDIS_SSL_CERT_REQS', 'required')
REDIS_SSL_CA_CERTS = _get_config_value('REDIS_SSL_CA_CERTS', None)
REDIS_USERNAME = _get_config_value('REDIS_USERNAME', os.getenv('REDIS_USERNAME', None))
REDIS_PASSWORD = _get_config_value('REDIS_PASSWORD', os.getenv('REDIS_PASSWORD', None))
REDIS_SELECT = _get_config_value('REDIS_SELECT', os.getenv('REDIS_SELECT', 0))
# 日志配置
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
LOG_FILE = 'trading_bot.log'