feat: implement dual-pipeline signal engine service
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled

New service at services/signal_engine/ implementing concurrent heuristic
(deterministic scoring) and probabilistic (Bayesian inference) pipelines
that evaluate technical signals across 6 timeframes (M30-M) and produce
independent BUY/WATCH/SKIP verdicts per ticker per evaluation tick.

Components:
- Input Normalizer: multi-source data assembly with sentinel fallbacks
- Signal Library: Fibonacci, MA Stack, RSI, Cup & Handle, Elliott Wave
- Multi-Timeframe Confluence Engine: weighted scoring with D/W/M anchors
- Hard Filter Engine: macro_bias, valuation, earnings proximity gating
- Heuristic Pipeline: S_total scoring with confidence-gated verdicts
- Probabilistic Pipeline: Bayesian log-odds with regime priors, entropy
  gating, EV_R calculation, and signal correlation penalty
- Exit Engine: stop-loss, targets, trailing ATR-based stops
- Delta Analyzer: pipeline agreement tracking with rolling Redis metrics
- Output Formatter: SignalOutput contract + Recommendation schema mapping
- Worker orchestrator: concurrent pipelines with failure isolation
- Main entry point: queue polling with fail-safe config loading

Infrastructure:
- Migration 039: signal_engine_outputs table with 3 indexes
- Helm chart: signalEngine service entry (processing tier)
- Redis key: QUEUE_SIGNAL_ENGINE constant

Tests: 390 tests (unit + property-based) covering all components
Config: dual_pipeline_enabled=false by default (safe rollout)
This commit is contained in:
Celes Renata
2026-05-02 07:32:26 +00:00
parent 7e2343ec2c
commit f468e30af0
61 changed files with 14107 additions and 184 deletions
+72
View File
@@ -0,0 +1,72 @@
# Feature: dual-pipeline-signal-engine, Property: Fibonacci retracement bounds
"""Property-based tests for the Fibonacci retracement formula.
Feature: dual-pipeline-signal-engine
Tests the Fibonacci retracement bounds property from the design specification:
for all retracement ratios r in [0, 1] and all swing high SH > swing low SL > 0,
the retracement level L(r) = SH - r * (SH - SL) must lie within [SL, SH].
Requirements: 2.1, 17.1
"""
from __future__ import annotations
from hypothesis import given, settings
from hypothesis import strategies as st
# ---------------------------------------------------------------------------
# Property: Fibonacci retracement bounds
# Validates: Requirements 2.1, 17.1
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Hypothesis strategies
# ---------------------------------------------------------------------------
# Retracement ratio in [0, 1]
_ratio = st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)
# Positive floats for swing high / swing low
_positive_float = st.floats(
min_value=1e-8, max_value=1e8, allow_nan=False, allow_infinity=False,
)
@st.composite
def _swing_pair(draw: st.DrawFn) -> tuple[float, float]:
"""Generate (SH, SL) where SH > SL > 0."""
a = draw(_positive_float)
b = draw(_positive_float)
sh = max(a, b)
sl = min(a, b)
# Ensure strict inequality SH > SL
if sh == sl:
sh = sl + 1e-8
return sh, sl
# ---------------------------------------------------------------------------
# Property test
# ---------------------------------------------------------------------------
@given(r=_ratio, swing=_swing_pair())
@settings(max_examples=100)
def test_fibonacci_retracement_within_bounds(r: float, swing: tuple[float, float]) -> None:
"""**Validates: Requirements 2.1, 17.1**
For all r in [0, 1] and all SH > SL > 0, the Fibonacci retracement
level L(r) = SH - r * (SH - SL) SHALL be in [SL, SH].
This is a pure mathematical property — no evaluator class needed.
"""
sh, sl = swing
# Compute the retracement level
level = sh - r * (sh - sl)
assert sl <= level <= sh, (
f"Fibonacci level {level} out of bounds [SL={sl}, SH={sh}] "
f"for r={r}.\n"
f" L(r) = {sh} - {r} * ({sh} - {sl}) = {level}"
)