Skip to content

Workflows & Jobs — Execution Reference

Last verified: 2026-04-02 (end-to-end run against local Docker stack)


Overview

The Redhound production pipeline runs as isolated Temporal workers communicating through Kafka.
There are two Temporal namespaces:

Namespace Purpose
trading On-demand analysis workflows (triggered by API/Kafka)
batch Recurring scheduled jobs (10 Temporal Schedules, created by job-worker on startup)

Analysis Pipeline (trading namespace)

AnalysisWorkflow

Worker: analyst-worker
Task Queue: analyst-task-queue
Trigger: Kafka analysis.trigger topic → trigger-consumer calls client.start_workflow()
Workflow ID pattern: analysis:<SYMBOL>:<event_id> (deterministic — deduplicates duplicate triggers)

Pipeline steps

1. FetchMarketData        → data-worker (data-task-queue)        max 2 min
2. 9× Analyst Activities  → analyst-worker (parallel fan-out)    max 3 min each
3. SignalAggregator       → analyst-worker                       max 1 min
4. AccuracyInjector       → analyst-worker                       max 30 sec
5. LangGraph Debate       → langgraph-worker                     max 8 min  (failure = partial result)
6. PersistResult          → result-consumer (result-task-queue)  max 2 min
7. PublishResult          → result-consumer                      max 1 min
8. UpdateSession          → result-consumer                      max 30 sec

Step details

Step Activity name Worker Purpose
1 run_fetch_market_data data-worker Download OHLCV, fundamentals, news, insider, sector data; write to Redis cache
2a run_technical_analyst analyst-worker Technical analysis: RSI, MACD, Bollinger, momentum, trend
2b run_sentiment_analyst analyst-worker Social/news sentiment via model-server (DeBERTa)
2c run_news_analyst analyst-worker News events, sentiment score, event count
2d run_fundamentals_analyst analyst-worker P/E, ROE, Piotroski score, DCF valuation
2e run_market_context_analyst analyst-worker Macro regime, rates, and VIX context
2f run_insider_analyst analyst-worker Insider buying/selling patterns
2g run_earnings_revisions_analyst analyst-worker EPS/revenue estimate revision trends
2h run_sector_analyst analyst-worker Sector momentum relative to broad market
2i run_valuation_comparables_analyst analyst-worker Relative valuation against peer comparables
3 run_signal_aggregator analyst-worker Merge 9 analyst scores using entropy-based confidence, VIX interpolation, RSI divergence, OBV confirmation
4 run_accuracy_injector analyst-worker Weight each analyst contribution by historical accuracy from leaderboard
5 run_langgraph langgraph-worker Bull/Bear LLM debate (1 round if confidence ≥75%, else 2; Starter plan skips it)
6 run_persist_result result-consumer Write final signal, decision, confidence to PostgreSQL + TimescaleDB
7 run_publish_result result-consumer Publish analysis.result Kafka event for downstream consumers
8 run_update_session result-consumer Update analysis session record in DB (status, completion time)

Output fields

Field Type Example
signal float [-1.0, 1.0] -0.6 (bearish)
confidence float [0.0, 1.0] 1.0
decision string BUY / SELL / HOLD
agents_completed list[str] ["news", "sector", ...]
agents_failed list[str] ["technical"] (rate-limited, etc.)
status string complete / partial

Partial result: If any analysts fail or LangGraph fails, the workflow continues with available data and sets status=partial. It never silently drops the entire run.

API-side result delivery

Once run_publish_result emits AnalysisCompletedEvent to Kafka, the API process closes the loop back to the browser:

  1. trigger-consumer (Kafka consumer, not a Temporal worker) bridges analysis.triggerclient.start_workflow(AnalysisWorkflow, ...).
  2. result-consumer persists the result and publishes AnalysisCompletedEventanalysis.completed.
  3. backend/api/workers_event_consumer.py (runs inside the API process) consumes analysis.completed, waits for the DB commit, then pushes WebSocket OUTPUT + STATUS_CHANGE:COMPLETED to connected frontend clients. It also records debate votes and evaluates signal alerts (best-effort).

The API never runs the pipeline itself — it only publishes triggers and consumes result events.


Recurring Jobs (batch namespace)

All job Temporal Schedules are created by job-worker at startup (_ensure_schedules()).
Overlap policy: SKIP — if a run is still executing when the next interval fires, the new run is skipped.
Retry policy: 3 attempts, initial backoff 5 s, max 10 min per execution.

Schedule Summary

Schedule ID Workflow Interval Purpose
price-checks PriceCheckWorkflow every 1 min Price alerts
scanner-checks ScannerCheckWorkflow every 1 min Scanner conditions
outcome-tracking OutcomeTrackingWorkflow every 5 min Signal outcome classification
index-intraday IndexIntradayWorkflow every 5 min Intraday index constituent refresh
hotlist-monitor HotlistMonitorWorkflow every 15 min Open paper position monitoring
catalyst-scan CatalystScanWorkflow every 30 min Options flow + short squeeze + earnings scan
weight-update WeightUpdateWorkflow every 60 min Adaptive analyst weight recalculation
universe-sweep UniverseSweepWorkflow every 24 h Full symbol universe pre-market sweep
action-plan-eval ActionPlanEvalWorkflow every 24 h Daily action plan evaluation
index-eod IndexEodWorkflow every 24 h End-of-day full index constituent download

Job Details

price-checks — PriceCheckWorkflow (every 1 min)

Activity: run_price_checks
Checks all active price-type alerts against live market prices. Fires notifications when a configured price threshold is crossed (e.g., "alert me when NVDA > $900").

scanner-checks — ScannerCheckWorkflow (every 1 min)

Activity: run_scanner_checks
Polls all active scanner configs and executes their scan cycles. Scanners define multi-condition filters (volume spike + RSI oversold, etc.) over the symbol universe; matches are surfaced to the user.

outcome-tracking — OutcomeTrackingWorkflow (every 5 min)

Activity: run_outcome_tracking
Fetches current prices for all pending (unresolved) signals and classifies their outcome as WIN / LOSS / NEUTRAL based on price movement since signal issuance. Results feed the analyst performance leaderboard.

index-intraday — IndexIntradayWorkflow (every 5 min)

Activity: run_index_intraday
Refreshes index constituent data (S&P 500, NASDAQ-100, etc.) during market hours. Keeps the tracked symbol universe current for intraday scanning.

hotlist-monitor — HotlistMonitorWorkflow (every 15 min)

Activity: run_hotlist_monitor
Monitors open paper trading positions against live prices. Checks stop-loss and take-profit levels, emits alerts when positions approach or breach those thresholds.

catalyst-scan — CatalystScanWorkflow (every 30 min)

Activity: run_catalyst_scan
Scans for high-impact catalysts across the universe: unusual options flow (large call/put sweeps), potential short-squeeze setups (high short interest + borrow rate), and upcoming earnings events within the next 7 days.

weight-update — WeightUpdateWorkflow (every 60 min)

Activity: run_weight_update
Recalculates each analyst's adaptive weight using the performance leaderboard. Analysts with higher historical accuracy on WIN classifications get higher weight in signal aggregation, improving overall signal quality over time.

universe-sweep — UniverseSweepWorkflow (every 24 h)

Activity: run_universe_sweep
Pre-market sweep of the full symbol universe. Identifies qualifying symbols (volume, market cap, momentum filters) and dispatches AnalysisTriggerEvent Kafka messages for each, seeding the daily analysis queue.

action-plan-eval — ActionPlanEvalWorkflow (every 24 h)

Activity: run_action_plan_eval
Evaluates all open ActionPlans against live prices. ActionPlans represent structured trade setups (entry, target, stop) generated by the LLM debate; this job checks whether plan conditions have been met or invalidated.

index-eod — IndexEodWorkflow (every 24 h)

Activity: run_index_eod
End-of-day download of full index constituent lists (S&P 500, NASDAQ-100, Russell 2000, etc.) from authoritative sources. Overwrites stale membership data to reflect rebalances and additions/removals.


Worker → Task Queue Mapping

Worker Task Queue Namespace Workflows registered Activities registered
analyst-worker analyst-task-queue trading AnalysisWorkflow 9 analyst activities + signal aggregator + accuracy injector
langgraph-worker langgraph-task-queue trading run_langgraph
data-worker data-task-queue trading run_fetch_market_data
result-consumer result-task-queue trading run_persist_result, run_publish_result, run_update_session
job-worker job-task-queue batch All 10 job workflows All 10 job activities
trigger-consumer (Kafka consumer) Bridges analysis.trigger Kafka → trading namespace AnalysisWorkflow

Verification Results (2026-04-02)

Component Status Evidence
Backend API Healthy GET /health{"status":"healthy","dependencies":{"database":"healthy","redis":"healthy"}}
Temporal (trading) Running AnalysisWorkflow for NVDA completed: status=COMPLETED, decision=HOLD, signal=-0.6, confidence=1.0
Temporal (batch) Running All 10 schedules active, recent execution times confirmed in Temporal UI
Kafka Running analysis.trigger topic reachable at kafka:9092 (internal) / localhost:29092 (host)
LangGraph debate Working run_langgraph completed and returned LangGraphActivityOutput correctly
Result persistence Working NVDA analysis written to DB + published to Kafka analysis.result topic
Model server (gRPC) Running Sentiment analyst connected via REDHOUND_MODEL_SERVER_ADDR
9 analyst activities completed A single analyst failing on a transient vendor rate limit is expected and retried on the next run; the workflow proceeds with status=partial