auto_trade_sys/trading_system/symbol_policy.py
薇薇安 1446bf852b feat(config): Introduce layered profit locking mechanism and manual trading adjustments
Added new configurations for a layered profit locking system, allowing for gradual profit protection at specified thresholds. Introduced manual trading options, including reduced and blocked symbol lists, to enhance trading strategy flexibility. Updated relevant backend and frontend components to reflect these changes, improving risk management and user control over trading activities.
2026-03-09 14:51:08 +08:00

135 lines
4.3 KiB
Python
Raw Permalink 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.

"""
交易对分层策略:
- 正常:按原策略处理
- reduced缩小仓位并在策略侧提高信号门槛
- blocked直接禁止自动开仓
"""
import logging
import re
from typing import Any, Dict, Set
logger = logging.getLogger(__name__)
def _parse_symbol_list(raw: Any) -> Set[str]:
"""兼容 string/list/dict 三种配置格式,统一转成大写 symbol 集合。"""
if raw is None:
return set()
if isinstance(raw, dict):
if "symbols" in raw:
return _parse_symbol_list(raw.get("symbols"))
if "items" in raw:
return _parse_symbol_list(raw.get("items"))
return set()
if isinstance(raw, str):
parts = re.split(r"[\s,;\n\r\t]+", raw)
return {p.strip().upper() for p in parts if p and p.strip()}
if isinstance(raw, list):
items: Set[str] = set()
for item in raw:
if isinstance(item, str):
sym = item.strip()
elif isinstance(item, dict):
sym = str(item.get("symbol") or item.get("value") or "").strip()
else:
sym = ""
if sym:
items.add(sym.upper())
return items
return set()
def _safe_float(value: Any, default: float) -> float:
try:
return float(value)
except (TypeError, ValueError):
return default
def _safe_int(value: Any, default: int) -> int:
try:
return int(value)
except (TypeError, ValueError):
return default
def resolve_symbol_trading_policy(symbol: str) -> Dict[str, Any]:
"""
合并手动名单与统计名单,返回统一的交易对分层结果。
返回字段:
- blocked: 是否禁止自动开仓
- reduced: 是否进入减仓观察名单
- position_factor: reduced 时建议的仓位系数
- signal_boost: reduced 时建议提高的最小信号门槛
- sources: 命中的来源,便于日志定位
"""
policy = {
"blocked": False,
"reduced": False,
"position_factor": 1.0,
"signal_boost": 0,
"sources": [],
}
symbol = (symbol or "").strip().upper()
if not symbol:
return policy
try:
from config_manager import GlobalStrategyConfigManager
mgr = GlobalStrategyConfigManager()
stats_symbol = mgr.get("STATS_SYMBOL_FILTERS", {}) or {}
manual_blocked = _parse_symbol_list(mgr.get("MANUAL_BLOCKED_SYMBOLS", ""))
manual_reduced = _parse_symbol_list(mgr.get("MANUAL_REDUCED_SYMBOLS", ""))
manual_position_factor = _safe_float(
mgr.get("MANUAL_REDUCED_SYMBOL_POSITION_FACTOR", 0.5), 0.5
)
manual_position_factor = max(0.05, min(manual_position_factor, 1.0))
manual_signal_boost = _safe_int(
mgr.get("MANUAL_REDUCED_SYMBOL_SIGNAL_BOOST", 1), 1
)
manual_signal_boost = max(0, min(manual_signal_boost, 5))
if symbol in manual_blocked:
policy["blocked"] = True
policy["sources"].append("manual_blocked")
if symbol in manual_reduced:
policy["reduced"] = True
policy["position_factor"] = min(policy["position_factor"], manual_position_factor)
policy["signal_boost"] = max(policy["signal_boost"], manual_signal_boost)
policy["sources"].append("manual_reduced")
for item in (stats_symbol.get("blacklist") or []):
if (item.get("symbol") or "").strip().upper() != symbol:
continue
mode = (item.get("mode") or "reduced").strip().lower()
if mode == "hard":
policy["blocked"] = True
policy["sources"].append("stats_hard")
else:
policy["reduced"] = True
policy["position_factor"] = min(policy["position_factor"], 0.5)
policy["signal_boost"] = max(policy["signal_boost"], 1)
policy["sources"].append("stats_reduced")
break
except Exception as e:
logger.debug(f"{symbol} 读取交易对分层策略失败: {e}")
if policy["blocked"]:
policy["reduced"] = False
policy["position_factor"] = 1.0
policy["signal_boost"] = 0
# 去重并保留顺序,便于日志阅读。
policy["sources"] = list(dict.fromkeys(policy["sources"]))
return policy