fix(orchestrator): code quality fixes in config and signals

- config: remove hardcoded absolute path for quant_backtest_path (now empty string)
- config: add llm_solo_penalty (0.7) and quant_solo_penalty (0.8) fields
- signals: SignalMerger now accepts OrchestratorConfig in __init__
- signals: use config.llm_solo_penalty / quant_solo_penalty instead of magic numbers
- signals: apply quant_weight_cap / llm_weight_cap as confidence upper bounds
- signals: both-None branch raises ValueError instead of returning ticker=""
- signals: replace assert with explicit ValueError for llm-None-when-quant-None
- signals: replace datetime.utcnow() with datetime.now(timezone.utc)
This commit is contained in:
陈少杰 2026-04-09 21:39:23 +08:00
parent 56dc76d44a
commit dacb3316fa
2 changed files with 21 additions and 16 deletions

View File

@ -3,9 +3,12 @@ from dataclasses import dataclass, field
@dataclass @dataclass
class OrchestratorConfig: class OrchestratorConfig:
quant_backtest_path: str = "/Users/chenshaojie/Downloads/quant_backtest" # Must be set to the local quant backtest output directory before use
quant_backtest_path: str = ""
trading_agents_config: dict = field(default_factory=dict) trading_agents_config: dict = field(default_factory=dict)
quant_weight_cap: float = 0.8 # quant 置信度上限 quant_weight_cap: float = 0.8 # quant 置信度上限
llm_weight_cap: float = 0.9 # llm 置信度上限 llm_weight_cap: float = 0.9 # llm 置信度上限
llm_batch_days: int = 7 # LLM 每隔几天运行一次(节省 API llm_batch_days: int = 7 # LLM 每隔几天运行一次(节省 API
cache_dir: str = "orchestrator/cache" # LLM 信号缓存目录 cache_dir: str = "orchestrator/cache" # LLM 信号缓存目录
llm_solo_penalty: float = 0.7 # LLM 单轨时的置信度折扣
quant_solo_penalty: float = 0.8 # Quant 单轨时的置信度折扣

View File

@ -1,8 +1,10 @@
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime from datetime import datetime, timezone
from typing import Optional from typing import Optional
from orchestrator.config import OrchestratorConfig
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -36,30 +38,27 @@ def _sign(x: float) -> int:
class SignalMerger: class SignalMerger:
def __init__(self, config: OrchestratorConfig) -> None:
self._config = config
def merge(self, quant: Optional[Signal], llm: Optional[Signal]) -> FinalSignal: def merge(self, quant: Optional[Signal], llm: Optional[Signal]) -> FinalSignal:
now = datetime.utcnow() now = datetime.now(timezone.utc)
# 两者均失败 # 两者均失败
if quant is None and llm is None: if quant is None and llm is None:
ticker = "" raise ValueError("both quant and llm signals are None")
return FinalSignal(
ticker=ticker,
direction=0,
confidence=0.0,
quant_signal=None,
llm_signal=None,
timestamp=now,
)
ticker = (quant or llm).ticker # type: ignore[union-attr] ticker = (quant or llm).ticker # type: ignore[union-attr]
# 只有 LLMquant 失败) # 只有 LLMquant 失败)
if quant is None: if quant is None:
assert llm is not None if llm is None:
raise ValueError("llm signal is None when quant is None")
return FinalSignal( return FinalSignal(
ticker=ticker, ticker=ticker,
direction=llm.direction, direction=llm.direction,
confidence=llm.confidence * 0.7, confidence=min(llm.confidence * self._config.llm_solo_penalty,
self._config.llm_weight_cap),
quant_signal=None, quant_signal=None,
llm_signal=llm, llm_signal=llm,
timestamp=now, timestamp=now,
@ -70,7 +69,8 @@ class SignalMerger:
return FinalSignal( return FinalSignal(
ticker=ticker, ticker=ticker,
direction=quant.direction, direction=quant.direction,
confidence=quant.confidence * 0.8, confidence=min(quant.confidence * self._config.quant_solo_penalty,
self._config.quant_weight_cap),
quant_signal=quant, quant_signal=quant,
llm_signal=None, llm_signal=None,
timestamp=now, timestamp=now,
@ -88,7 +88,9 @@ class SignalMerger:
ticker, ticker,
) )
total_conf = quant.confidence + llm.confidence total_conf = quant.confidence + llm.confidence
final_confidence = abs(weighted_sum) / total_conf if total_conf > 0 else 0.0 raw_confidence = abs(weighted_sum) / total_conf if total_conf > 0 else 0.0
final_confidence = min(raw_confidence, self._config.quant_weight_cap,
self._config.llm_weight_cap)
return FinalSignal( return FinalSignal(
ticker=ticker, ticker=ticker,