trading-signals

repository·main·Indexed 21 days ago

https://github.com/bennycode/trading-signals

A modular, fully-typed TypeScript toolkit for building algorithmic trading bots. It includes the @typedtrader/exchange package for unified broker abstractions (supporting Alpaca and Trading212), @typedtrader/messaging for Telegram-controlled execution, and a library of technical indicators with streaming data support and stability tracking.

Tokens
38.2K
Snippets
113
Records
178
Agent score
75%

What's inside trading-signals

  1. Overview of Trading Signals packages

    main

    The monorepo is composed of several independent packages that can be used individually or together to build a complete trading stack:

    • trading-signals: Provides technical indicators (SMA, EMA, RSI, MACD, Bollinger Bands, etc.) with streaming updates, replace mode, and lazy evaluation.
    • trading-strategies: Combines indicators into actionable advice. Includes a library of ready-to-use strategies and a ProtectedStrategy base class for stop-loss and take-profit management.
    • @typedtrader/exchange: A unified, type-safe interface for brokers (e.g., Alpaca). Supports WebSocket streaming for candles and order updates, and handles both paper and live trading.
    • @typedtrader/messaging: A Telegram-based remote control layer for managing accounts, running strategies, and inspecting live state via chat. Uses SQLite and Drizzle ORM for persistence.
    • trading-signals-docs: A Next.js-based interactive showcase and visual backtester located at typedtrader.com.
  2. Understand Market Regimes in Trading Strategies

    main

    The trading-strategies package uses the concept of Market Regimes to classify market states. This classification helps determine which strategies are applicable to a specific asset at a given time. A regime is defined by crossing direction (uptrending, downtrending, or ranging) with volatility (low or high).

    Regime Matrix

    DirectionLow VolatilityHigh Volatility
    UptrendingSmooth uptrend (trend-following longs)Volatile uptrend (breakout longs, wide stops)
    DowntrendingSmooth downtrend (trend-following shorts)Volatile downtrend (breakout shorts)
    RangingTight range (wait / accumulate)Wide range (mean reversion, scalping)

    Implementation Details

    • Strategies: Use regimes as static metadata to declare which market states they target.
    • Stocks: Receive dynamic regime labels computed from recent price action (e.g., using Efficiency Ratio for direction and ATR% for volatility).
    • Noise: A state where price movement has neither direction nor meaningful range; strategies should generally avoid these periods.
  3. How the Trading Signals ecosystem works together

    main

    The project is designed as a layered stack where each component builds on the previous one:

    1. trading-signals (Foundation): Provides the raw technical indicators.
    2. trading-strategies (Logic): Uses indicators to generate OrderAdvice.
    3. @typedtrader/exchange (Execution): Abstracts broker-specific APIs so strategies can execute trades regardless of the broker.
    4. @typedtrader/messaging (Interface): Provides a high-level chatbot interface to manage TradingSessions (which pair a strategy with a broker) or to run BacktestExecutors (which replay historical candles).
  4. How ProtectedStrategy works as a kill-switch

    main

    ProtectedStrategy is an abstract base class that adds composable kill-switch behavior (stop-loss and take-profit) to any trading strategy. It acts as a guard layer that sits above your custom strategy logic.

    Lifecycle Phases

    • active: The strategy tracks position cost basis via onFill. On each candle, it checks if any configured guard (stop-loss or take-profit) has been tripped.
    • tripped: A guard has fired. The class emits a SELL advice (either LIMIT or MARKET depending on configuration). The subclass's logic is not called.
    • retrying: The position has not yet been fully exited. The class continues to re-emit the same exit advice on every candle until onFill clears the position.
    • closed: The position is fully exited. onCandle returns void for the remainder of the session and the kill-switch is terminal (it does not re-arm).

    Implementation Pattern

    To use it, extend ProtectedStrategy and call super.processCandle() at the very beginning of your own processCandle implementation. If the super call returns advice, you must return it immediately to prevent your strategy logic from running while the kill-switch is active.

    // Inside your subclass
    protected override async processCandle(candle, state) {
      const guardAdvice = await super.processCandle(candle, state);
      if (guardAdvice) {
        return guardAdvice; // kill switch fired — return immediately
      }
    
      // your custom strategy logic goes here
    }
  5. How the Broker abstraction works

    main

    The @typedtrader/exchange package provides a unified Broker contract that allows you to write trading strategies against a single API regardless of the underlying broker (e.g., Alpaca or Trading212). This abstraction covers:

    • Order Management: Market and limit orders, long and short positions.
    • Account Data: Listing balances and positions.
    • Market Data: Watching fills and streaming candles.
    • Fee Awareness: Using getFeeRates() and estimateFee() to account for costs like currency conversion.

    Because it uses a consistent interface, a strategy written for one broker can be run on any supported broker without modification.

  6. Architecture of a Broker Integration

    main

    A broker integration follows a layered architecture to ensure each component has a single responsibility and can be replaced independently. The hierarchy flows from high-level strategy down to low-level transport:

    1. Strategy: Uses neutral domain types.
    2. Broker: The neutral interface (defined in Broker.ts).
    3. Mapper: Translates wire-format data to neutral domain types.
    4. Schema: Uses zod to parse and validate data at the boundary.
    5. API Class: One method per endpoint; thin wrappers around HTTP calls.
    6. RESTClient / WebSocket Manager: Handles auth, retries, and reconnections.
    7. Transport: The underlying axios or native WebSocket implementation.
  7. Implement the Mapper Layer and Invariants

    main

    The XxxBrokerMapper translates wire-format data into neutral domain types (e.g., Candle, Fill, PendingOrder). To maintain consistency, follow these invariants:

    • Unsigned Sizes: Neutral types use unsigned size. Use Math.abs() on signed wire quantities so a SELL doesn't result in a negative size.
    • Side Derivation: If the wire format provides multiple representations of an order (e.g., quantity vs value), fall back across all sources and derive the side from whichever signed field is populated.
    • Filter, Don't Coerce: If a broker returns an order type not supported by the neutral enum (e.g., STOP_LIMIT when only MARKET/LIMIT are modeled), drop the record rather than attempting to downgrade it.
    • Fee Currency: The fee asset should be the account's base currency, not the instrument's counter currency. Pass the account currencyCode into the mapper.
  8. Rate Limiting and Retries

    main

    The package handles broker rate limits automatically:

    • Automatic Retries: Uses axios-retry with delays calibrated to specific broker endpoints (e.g., Trading212 account cash is limited to 1 req / 2s).
    • Smart Polling: For brokers without WebSockets (like Trading212), watchOrders polls at intervals that match the broker's documented limits.
    • Error Handling: Transient network errors (e.g., EAI_AGAIN, HTTP 429/5xx) are retried transparently. Non-retryable errors (401, 403, or validation failures) are surfaced immediately.
  9. Use the Neutral Broker Base Class

    main

    The Broker class is responsible for brokerage capabilities (orders, fills, balances, etc.). It is distinct from market data.

    • Market Data Dependency: Every Broker must take a mandatory marketData: MarketDataSource in its constructor. It delegates all candle-related methods (getCandles, watchCandles, etc.) to this injected source.
    • Lifecycle: A broker's disconnect() method should only close trading-side connections. It should not close an injected MarketDataSource. The owner of the MarketDataSource is responsible for its lifecycle.
    • Abstraction: Strategies should depend on the Broker or MarketDataSource interfaces, never on a concrete broker implementation.
    • Shutdown: Implement disconnect() as the single seam to clean up all async resources owned by the class.
  10. Run market analysis with Reports

    main

    The library provides Report implementations that analyze market data and return formatted results. Reports can be run on-demand or scheduled. It is recommended to add a fingerprint (a short hash of the raw result data) to reports so that if two runs share the same fingerprint, no changes occurred.

    Available reports:

    • SP500MomentumReport: Ranks the S&P 500 by 12-1 cross-sectional momentum.
    • SP500HeatmapReport: Provides a snapshot of S&P 500 performance.
    • ScalpScannerReport: Scans for short-term scalping opportunities.
  11. Understand technical indicator types and classifications

    main

    The library implements indicators that can be classified by several dimensions:

    By Function

    • Momentum indicators: Measure the speed and strength of price movements (e.g., identifying overbought/oversold conditions).
    • Trend indicators: Measure the direction of a trend (bullish/bearish).
    • Volatility indicators: Measure the degree of price variation over time.
    • Volume indicators: Measure trend strength based on volume.

    By Timing

    • Leading Indicators: Predictive tools that attempt to signal future movements (e.g., RSI, Stochastic Oscillator).
    • Lagging Indicators: Confirmative tools that signal after a move has started (e.g., Moving Averages, MACD).

    By Scale

    • Indicators: Have no fixed upper or lower limits.
    • Oscillators: Move within a fixed range (e.g., 0 to 100, or -1 to +1).