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
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)
quant_weight_cap: float = 0.8 # quant 置信度上限
llm_weight_cap: float = 0.9 # llm 置信度上限
llm_batch_days: int = 7 # LLM 每隔几天运行一次(节省 API
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
from dataclasses import dataclass, field
from datetime import datetime
from datetime import datetime, timezone
from typing import Optional
from orchestrator.config import OrchestratorConfig
logger = logging.getLogger(__name__)
@ -36,30 +38,27 @@ def _sign(x: float) -> int:
class SignalMerger:
def __init__(self, config: OrchestratorConfig) -> None:
self._config = config
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:
ticker = ""
return FinalSignal(
ticker=ticker,
direction=0,
confidence=0.0,
quant_signal=None,
llm_signal=None,
timestamp=now,
)
raise ValueError("both quant and llm signals are None")
ticker = (quant or llm).ticker # type: ignore[union-attr]
# 只有 LLMquant 失败)
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(
ticker=ticker,
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,
llm_signal=llm,
timestamp=now,
@ -70,7 +69,8 @@ class SignalMerger:
return FinalSignal(
ticker=ticker,
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,
llm_signal=None,
timestamp=now,
@ -88,7 +88,9 @@ class SignalMerger:
ticker,
)
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(
ticker=ticker,