Skip to content

Event-driven strategy triggers + news/event data providers (push & polled) #534

@MDUYN

Description

@MDUYN

Summary

Today every TradingStrategy is woken on a fixed bar/time schedule (time_unit + interval). This makes a whole family of event-reactive strategies — most importantly news / NLP / on-chain event reactions — awkward or impossible to express, because the relevant event is the arrival of a message, not the close of a bar.

This issue proposes two related additions that together unlock that strategy family:

  1. A trigger abstraction so a strategy can declare "run me when X happens" in addition to (or instead of) "run me every N minutes".
  2. A news / event data provider family with two concrete implementations:
    • Polled (every N seconds, hit a REST endpoint / RSS / API).
    • Pushed (subscribe to a websocket / SSE feed; deliver messages as they arrive).

The goal is second-level reactivity (typical news-API latency is 100ms–few seconds), not true sub-millisecond HFT. That makes it a tractable Python-level addition.

Motivation

See examples/strategies_showcase/25_news_nlp_subsecond/README.md. The README currently says "not possible" because the framework lacks an event-driven trigger model. With a small additive change this becomes a 🟢 workable strategy type covering:

  • Macro release reactions (CPI, FOMC, etc.).
  • Listing / delisting announcements.
  • Hack / exploit alerts on on-chain feeds.
  • Sentiment-spike trades on social-media firehoses.
  • Any "if event of type X arrives, evaluate strategy" workflow.

Proposed design (sketch)

1. Trigger abstraction

from investing_algorithm_framework import (
    TradingStrategy, EventTrigger, IntervalTrigger, TimeUnit,
)

class NewsReactionStrategy(TradingStrategy):
    triggers = [
        IntervalTrigger(time_unit=TimeUnit.MINUTE, interval=5),  # heartbeat
        EventTrigger(source="news", filter=lambda evt: evt.symbol == "BTC"),
    ]
    data_sources = [...]

    def run_strategy(self, context, data, event=None):
        if event is not None:
            # Fired by EventTrigger — react immediately.
            ...
        else:
            # Fired by the heartbeat — periodic housekeeping.
            ...

time_unit + interval remain for backward compatibility (sugar for a single IntervalTrigger).

2. Event data provider family

class EventDataProvider(DataProvider):
    """Base class for providers that emit discrete events."""
    async def stream(self) -> AsyncIterator[Event]: ...

class PolledNewsDataProvider(EventDataProvider):
    """Polls an HTTP endpoint every N seconds, emits new items."""

class WebSocketEventDataProvider(EventDataProvider):
    """Subscribes to a websocket; emits each message as an Event."""

A new EventBus inside the runtime owns the event loop, fans out events to all registered EventTriggers, and invokes the matching strategies' run_strategy(..., event=evt).

3. Backtest semantics

For backtests, an EventDataProvider can expose a historical event tape (CSV/JSON of timestamped events). The backtest engine merges that tape with bar timestamps and replays them in chronological order, calling run_strategy once per replayed event — this gives faithful in-sample replay of event-driven strategies.

Out of scope

  • True sub-millisecond HFT (still belongs in a separate iaf_realtime runtime).
  • L2 order-book streaming (separate issue).
  • Per-event kernel-bypass / colocation concerns.

Acceptance criteria

  • Trigger abstract base + IntervalTrigger + EventTrigger implementations.
  • triggers class attribute on TradingStrategy; falls back to a single IntervalTrigger from the existing time_unit/interval if not set (backward compatible).
  • EventDataProvider base + at least one concrete provider (PolledNewsDataProvider).
  • EventBus in the live runtime; events are dispatched to matching triggers.
  • Backtest replays a historical event tape merged with bar timestamps.
  • Showcase: convert examples/strategies_showcase/25_news_nlp_subsecond from 🔴 to 🟢 with a runnable example (e.g. polled crypto-news API → simple sentiment threshold → BTC trade).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions