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
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:
@@ -0,0 +1,127 @@
|
||||
"""Fibonacci retracement signal evaluator.
|
||||
|
||||
Computes retracement levels using ``L(r) = SH - r * (SH - SL)`` for the
|
||||
standard ratios [0.236, 0.382, 0.5, 0.618, 0.786] and produces a signal
|
||||
based on the proximity of the current price to the nearest level.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from services.signal_engine.models import OHLCVBar, SignalDirection, SignalResult
|
||||
from services.signal_engine.signals.base import (
|
||||
find_swing_high,
|
||||
find_swing_low,
|
||||
validate_lookback,
|
||||
)
|
||||
|
||||
# Standard Fibonacci retracement ratios
|
||||
RETRACEMENT_RATIOS: list[float] = [0.236, 0.382, 0.5, 0.618, 0.786]
|
||||
|
||||
# Ratios considered "key" levels — proximity to these yields higher confidence
|
||||
_KEY_RATIOS: set[float] = {0.5, 0.618}
|
||||
|
||||
# Default minimum number of bars required for evaluation
|
||||
DEFAULT_MIN_BARS: int = 20
|
||||
|
||||
|
||||
class FibonacciEvaluator:
|
||||
"""Fibonacci retracement signal evaluator.
|
||||
|
||||
Satisfies the :class:`~services.signal_engine.signals.base.SignalEvaluator`
|
||||
protocol.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
min_bars:
|
||||
Minimum number of OHLCV bars required before the evaluator will
|
||||
produce a signal. Defaults to ``20``.
|
||||
"""
|
||||
|
||||
def __init__(self, min_bars: int = DEFAULT_MIN_BARS) -> None:
|
||||
self.min_bars = min_bars
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Public API (SignalEvaluator protocol)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def evaluate(
|
||||
self,
|
||||
bars: list[OHLCVBar],
|
||||
timeframe: str,
|
||||
) -> SignalResult | None:
|
||||
"""Evaluate Fibonacci retracement on *bars* for *timeframe*.
|
||||
|
||||
Returns ``None`` when there are fewer than :pyattr:`min_bars` bars,
|
||||
or when the swing high equals the swing low (flat market — no valid
|
||||
retracement).
|
||||
"""
|
||||
if not validate_lookback(bars, self.min_bars):
|
||||
return None
|
||||
|
||||
# Detect swing high / swing low within the evaluation window
|
||||
sh_result = find_swing_high(bars, self.min_bars)
|
||||
sl_result = find_swing_low(bars, self.min_bars)
|
||||
|
||||
if sh_result is None or sl_result is None:
|
||||
return None
|
||||
|
||||
_sh_idx, sh_price = sh_result
|
||||
_sl_idx, sl_price = sl_result
|
||||
|
||||
# SH must be strictly greater than SL for a valid retracement range
|
||||
if sh_price <= sl_price:
|
||||
return None
|
||||
|
||||
price_range = sh_price - sl_price
|
||||
current_price = bars[-1].close
|
||||
|
||||
# Compute retracement levels: L(r) = SH - r * (SH - SL)
|
||||
levels: dict[float, float] = {
|
||||
r: sh_price - r * price_range for r in RETRACEMENT_RATIOS
|
||||
}
|
||||
|
||||
# Find the nearest retracement level to the current price
|
||||
nearest_ratio: float = RETRACEMENT_RATIOS[0]
|
||||
nearest_level: float = levels[nearest_ratio]
|
||||
min_distance: float = abs(current_price - nearest_level)
|
||||
|
||||
for ratio in RETRACEMENT_RATIOS[1:]:
|
||||
distance = abs(current_price - levels[ratio])
|
||||
if distance < min_distance:
|
||||
min_distance = distance
|
||||
nearest_ratio = ratio
|
||||
nearest_level = levels[ratio]
|
||||
|
||||
# Signal strength: 1.0 - (distance / range), clamped to [0, 1]
|
||||
raw_strength = 1.0 - (min_distance / price_range)
|
||||
strength = max(0.0, min(1.0, raw_strength))
|
||||
|
||||
# Direction: BULLISH if price is near a retracement level and above SL
|
||||
# (potential bounce off support). Otherwise BEARISH.
|
||||
if current_price >= sl_price:
|
||||
direction = SignalDirection.BULLISH
|
||||
else:
|
||||
direction = SignalDirection.BEARISH
|
||||
|
||||
# Confidence: higher when the nearest level is a key ratio (0.618, 0.5)
|
||||
if nearest_ratio in _KEY_RATIOS:
|
||||
confidence = min(1.0, strength * 1.2)
|
||||
else:
|
||||
confidence = strength * 0.8
|
||||
|
||||
return SignalResult(
|
||||
signal_type="fibonacci",
|
||||
timeframe=timeframe,
|
||||
strength=strength,
|
||||
direction=direction,
|
||||
confidence=confidence,
|
||||
metadata={
|
||||
"swing_high": sh_price,
|
||||
"swing_low": sl_price,
|
||||
"retracement_levels": levels,
|
||||
"nearest_ratio": nearest_ratio,
|
||||
"nearest_level": nearest_level,
|
||||
"distance_to_nearest": min_distance,
|
||||
"current_price": current_price,
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user