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:
- A trigger abstraction so a strategy can declare "run me when X happens" in addition to (or instead of) "run me every N minutes".
- 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
Related
Summary
Today every
TradingStrategyis 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:
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:Proposed design (sketch)
1. Trigger abstraction
time_unit+intervalremain for backward compatibility (sugar for a singleIntervalTrigger).2. Event data provider family
A new
EventBusinside the runtime owns the event loop, fans out events to all registeredEventTriggers, and invokes the matching strategies'run_strategy(..., event=evt).3. Backtest semantics
For backtests, an
EventDataProvidercan 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, callingrun_strategyonce per replayed event — this gives faithful in-sample replay of event-driven strategies.Out of scope
iaf_realtimeruntime).Acceptance criteria
Triggerabstract base +IntervalTrigger+EventTriggerimplementations.triggersclass attribute onTradingStrategy; falls back to a singleIntervalTriggerfrom the existingtime_unit/intervalif not set (backward compatible).EventDataProviderbase + at least one concrete provider (PolledNewsDataProvider).EventBusin the live runtime; events are dispatched to matching triggers.examples/strategies_showcase/25_news_nlp_subsecondfrom 🔴 to 🟢 with a runnable example (e.g. polled crypto-news API → simple sentiment threshold → BTC trade).Related
examples/strategies_showcase/README.md