Component · L2

Alpha Signals

github.com/juanmmm21/alpha-signal-generator

Pluggable strategy engine that emits strict entry and exit signals from market data.

Lines
926
Files
24
Test files
6
Commits
16
Started
29 Jun 2026
Last commit
6 Jul 2026
  • Python 100%

● Where it sits

Layer 2 of Quant — analytics.

Decoupled strategy engine that analyzes OHLCV candles, technical indicators, and optional order book metrics to emit strict entry and exit events. Fifth module of the quant-core-infra ecosystem.

Repository: github.com/juanmmm21/alpha-signal-generator

What it is and what problem it solves

A trading bot needs to translate market data into discrete decisions: enter, exit, or hold. Mixing that logic with data ingestion, order execution, or backtesting produces fragile, untestable code.

This module isolates the decision logic behind a clear contract:

  • Each strategy implements evaluate(context) → SignalEvent
  • The engine maintains position state per strategy
  • The output is a typed event with action, side, confidence, and reason

Adding a new strategy = one class + registration in registry.py. No need to touch the engine or the pipeline.


Role in quant-core-infra

market-data-lakehouse ──┐
ta-indicators-from-scratch ──► candles + indicators ──► alpha-signal-generator
order-book-reconstructor ──┘         (book metrics)         │
                                                               ▼
                                                    event-driven-backtester

It is the decision brain between data analysis and simulation/execution.


Objective

Demonstrates:

  • Decoupled design with Protocol and an abstract base class
  • Strict SignalEvent contracts for downstream systems
  • Multi-source context (candles, indicators, book)
  • JSONL ingestion compatible with upstream modules

Included strategies

IDEntry logicExit logic
rsi_mean_reversionRSI ≤ oversold threshold (30)RSI ≥ neutral (50) or overbought (70)
macd_crossoverMACD crosses above signalMACD crosses below signal
book_imbalanceBid-heavy imbalance + tight spreadImbalance falls below exit threshold

All manage long positions only in this initial version.


How it works

  1. Ingestion: JSONL candles are loaded with OHLCV fields + optional indicators.
  2. Context: for each bar, a StrategyContext is built with the current bar, previous bar, open position, and optional book.
  3. Evaluation: each registered strategy produces a SignalEvent.
  4. State: SignalEngine updates internal position on enter / exit.
  5. Output: the pipeline filters hold events and serializes only actionable signals.

SignalEvent contract

FieldValuesDescription
actionenter, exit, holdThe strategy's decision
sidelong, short, nullRequired on enter/exit
confidence0.0 – 1.0Conviction of the signal
reasonstrAuditable text of the decision
reference_priceDecimalReference price (bar close)

Architecture

Candles + indicators (JSONL)
        │
        ├─ optional book metrics (JSONL)
        ▼
StrategyContext (bar, previous_bar, position, book)
        │
        ▼
SignalEngine
   ├─ RsiMeanReversionStrategy
   ├─ MacdCrossoverStrategy
   └─ OrderBookImbalanceStrategy
        │
        ▼
SignalEvent

Components

ModuleResponsibility
base.pyBaseStrategy with _enter, _exit, _hold helpers
protocols.pyTradingStrategy protocol
strategies/Concrete implementations
engine.pyPosition state and bar-by-bar evaluation
registry.pycreate_strategy(name) factory
ingest.py / pipeline.pyJSONL → serialized signals

Technical decisions

  • Decimal for reference prices and book metrics
  • float for TA indicators (produced upstream)
  • One position per strategy — no netting between strategies
  • Actionable output — hold never appears in the pipeline's JSON

How to add a strategy

from alpha_signal_generator.base import BaseStrategy
from alpha_signal_generator.models import SignalEvent, StrategyContext
from alpha_signal_generator.registry import STRATEGY_REGISTRY


class MyStrategy(BaseStrategy):
    def evaluate(self, context: StrategyContext) -> SignalEvent:
        # your logic here
        return self._hold(context.bar, "waiting for setup")


STRATEGY_REGISTRY["my_strategy"] = MyStrategy

Requirements

  • Python 3.11+

Installation

cd alpha-signal-generator
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

CLI usage

# List strategies
alpha-signal-generator list

# RSI mean reversion
alpha-signal-generator run \
  --candles samples/btcusdt_candles_with_indicators.jsonl \
  --symbol BTCUSDT \
  --strategy rsi_mean_reversion

# With order book confirmation
alpha-signal-generator run \
  --candles samples/btcusdt_candles_with_indicators.jsonl \
  --books samples/btcusdt_book_metrics.jsonl \
  --symbol BTCUSDT \
  --strategy book_imbalance \
  --output signals.json

JSONL formats

Candles with indicators

{
  "open": 100.5,
  "high": 101.0,
  "low": 99.5,
  "close": 100.8,
  "volume": 12.0,
  "event_time": "2024-01-01T12:00:00Z",
  "rsi": 28.5,
  "macd_line": -0.12,
  "signal_line": -0.08
}

Extra fields (rsi, macd_line, etc.) are automatically parsed as indicators.

Book metrics

{
  "symbol": "BTCUSDT",
  "best_bid": "100.4",
  "best_ask": "100.6",
  "spread": "0.2",
  "mid_price": "100.5",
  "bid_depth": "12.5",
  "ask_depth": "8.0",
  "imbalance": 0.32,
  "event_time": "2024-01-01T12:00:00Z"
}

Programmatic usage

from alpha_signal_generator import (
    MacdCrossoverStrategy,
    RsiMeanReversionStrategy,
    SignalEngine,
    run_signal_pipeline,
)

# Full pipeline from files
signals = run_signal_pipeline(
    candles_path="candles.jsonl",
    strategy_name="macd_crossover",
    symbol="BTCUSDT",
)

# Manual engine with multiple strategies
engine = SignalEngine([
    RsiMeanReversionStrategy(),
    MacdCrossoverStrategy(),
])

Development

pytest -q
ruff check src tests
mypy src

Roadmap

  • Short and hedging strategies
  • Strategy composition (voting, weighting)
  • Real-time integration with candle stream

License

MIT

esc
↑↓ navigate↵ open